#!/bin/sh
-# The base path for the installation.
-# This is typcially /usr or /usr/local.
-INSTALL_DIR="/usr"
-
-# Where the core libraries for Onyx will go.
-CORE_DIR="$INSTALL_DIR/share/onyx"
-
-# Where the onyx executable will be placed.
-BIN_DIR="$INSTALL_DIR/bin"
-
-# The compiler to use. Only GCC and TCC have been tested.
-CC='gcc'
-
-# The architecture of your system. If your not sure, leave this alone.
-ARCH="$(uname -m)"
-
-# RUNTIME_LIBRARY="ovmwasm"
-RUNTIME_LIBRARY="wasmer"
-
-# Comment this line if you do not want the above library installed,
-# and do not with to have the Onyx runtime.
-ENABLE_BUNDLING_WASM_RUNTIME=1
-
-# Where the Wasmer library files can be found.
-# They are bundled with the project, but if a different version is available, these can be changed.
-WASMER_INCLUDE_DIR="$(pwd)/lib/common/include"
-WASMER_LIBRARY_DIR="$(pwd)/lib/linux_$ARCH/lib"
-
-# Where the intermediate build files go.
-BUILD_DIR='./build'
-
-# Temporary flag
-ENABLE_DEBUG_INFO=0
-
-
-
+. ./settings.sh
echo "Installing core libs"
sudo mkdir -p "$CORE_DIR"
sudo cp -r ./core/ "$CORE_DIR"
-# This is a development feature to allow for quickly reinstalling core libraries
-# without have to recompile the entire compiler
-[ "$1" = "core" ] && exit 0
+if [ ! -f "$CORE_DIR/lib/lib$RUNTIME_LIBRARY.so" ] || true; then
+ echo "Copying lib$RUNTIME_LIBRARY to $CORE_DIR/lib (first install)"
+ sudo mkdir -p "$CORE_DIR/lib"
+ sudo mkdir -p "$CORE_DIR/include"
-C_FILES="onyx astnodes builtins checker clone doc entities errors lex parser symres types utils wasm_emit"
-LIBS=
-INCLUDES="-I./include"
+ sudo cp "$WASMER_LIBRARY_DIR/lib$RUNTIME_LIBRARY.so" "$CORE_DIR/lib/lib$RUNTIME_LIBRARY.so"
-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'
-
-TARGET="./bin/onyx"
-if [ "$1" = "debug" ]; then
- FLAGS="$WARNINGS -g3"
-else
- FLAGS="$WARNINGS -O3"
-fi
-
-if [ "$RUNTIME_LIBRARY" = "ovmwasm" ]; then
- FLAGS="$FLAGS -DUSE_OVM_DEBUGGER"
+ sudo cp "shared/include/onyx_library.h" "$CORE_DIR/include/onyx_library.h"
+ sudo cp "$WASMER_INCLUDE_DIR/wasm.h" "$CORE_DIR/include/wasm.h"
fi
-if [ "$ENABLE_DEBUG_INFO" = "1" ]; then
- FLAGS="$FLAGS -DENABLE_DEBUG_INFO"
-fi
-
-
-if [ ! -z "$ENABLE_BUNDLING_WASM_RUNTIME" ]; then
- C_FILES="$C_FILES wasm_runtime"
- FLAGS="$FLAGS -DENABLE_RUN_WITH_WASMER"
- LIBS="$LIBS -L$CORE_DIR/lib -l$RUNTIME_LIBRARY -Wl,-rpath=$CORE_DIR/lib:./ -lpthread -ldl -lm"
- INCLUDES="$INCLUDES -I$WASMER_INCLUDE_DIR"
-
- if [ ! -f "$CORE_DIR/lib/lib$RUNTIME_LIBRARY.so" ] || true; then
- echo "Copying lib$RUNTIME_LIBRARY to $CORE_DIR/lib (first install)"
-
- sudo mkdir -p "$CORE_DIR/lib"
- sudo mkdir -p "$CORE_DIR/include"
-
- sudo cp "$WASMER_LIBRARY_DIR/lib$RUNTIME_LIBRARY.so" "$CORE_DIR/lib/lib$RUNTIME_LIBRARY.so"
-
- sudo cp "include/onyx_library.h" "$CORE_DIR/include/onyx_library.h"
- sudo cp "lib/common/include/wasm.h" "$CORE_DIR/include/wasm.h"
- fi
-fi
-
-mkdir -p "$BUILD_DIR"
-
-compile() {
- for file in $C_FILES ; do
- echo "Compiling $file.c"
- $CC -o $BUILD_DIR/$file.o \
- $FLAGS \
- "-DCORE_INSTALLATION=\"$CORE_DIR\"" \
- -c src/$file.c \
- $INCLUDES $LIBS
- done
-
- echo "Linking $TARGET"
- $CC -o $TARGET $FLAGS $(for file in $C_FILES ; do printf "$BUILD_DIR/%s.o " $file ; done) $LIBS
-
- echo "Removing object files"
- for file in $C_FILES ; do rm -f "$BUILD_DIR/$file".o 2>/dev/null ; done
-}
-
-compile
-echo "Installing onyx executable"
-sudo mkdir -p "$BIN_DIR"
-sudo cp ./bin/onyx "$BIN_DIR/onyx"
-
-if [ ! -z "$ENABLE_BUNDLING_WASM_RUNTIME" ]; then
- C_FILES="onyxrun wasm_runtime"
- TARGET="./bin/onyx-run"
-
- compile
- echo "Installing onyxrun executable"
- sudo cp ./bin/onyx-run "$BIN_DIR/onyx-run"
+# This is a development feature to allow for quickly reinstalling core libraries
+# without have to recompile the entire compiler
+[ "$1" = "core" ] && exit 0
- $CC -shared -fpic -I include -I lib/common/include src/onyx_runtime.c -o onyx_runtime.so -lpthread
- sudo mv "./onyx_runtime.so" "$CORE_DIR/lib/onyx_runtime.so"
+sudo cp ./bin/onyx-pkg "$BIN_DIR/onyx-pkg"
+sudo mkdir -p "$CORE_DIR/tools"
+sudo cp ./scripts/onyx-pkg.onyx "$CORE_DIR/tools"
+cd compiler
+./build.sh
+cd ..
- sudo cp ./bin/onyx-pkg "$BIN_DIR/onyx-pkg"
- sudo mkdir -p "$CORE_DIR/tools"
- sudo cp ./scripts/onyx-pkg.onyx "$CORE_DIR/tools"
-fi
+cd runtime
+./build.sh
+cd ..
# Otherwise the prompt ends on the same line
printf "\n"
--- /dev/null
+#!/bin/sh
+
+. ../settings.sh
+
+# Temporary flag
+ENABLE_DEBUG_INFO=1
+
+C_FILES="onyx astnodes builtins checker clone doc entities errors lex parser symres types utils wasm_emit wasm_runtime "
+LIBS="-L$CORE_DIR/lib -l$RUNTIME_LIBRARY -Wl,-rpath=$CORE_DIR/lib:./ -lpthread -ldl -lm"
+INCLUDES="-I./include -I../shared/include -I../lib/common/include"
+
+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'
+
+if [ "$1" = "debug" ]; then
+ FLAGS="$WARNINGS -g3"
+else
+ FLAGS="$WARNINGS -O3"
+fi
+
+if [ "$RUNTIME_LIBRARY" = "ovmwasm" ]; then
+ FLAGS="$FLAGS -DUSE_OVM_DEBUGGER"
+fi
+
+if [ "$ENABLE_DEBUG_INFO" = "1" ]; then
+ FLAGS="$FLAGS -DENABLE_DEBUG_INFO"
+fi
+
+FLAGS="$FLAGS -DENABLE_RUN_WITH_WASMER"
+
+sudo mkdir -p "$BIN_DIR"
+
+echo "Compiling onyx..."
+$CC -o "../bin/onyx" \
+ $FLAGS \
+ "-DCORE_INSTALLATION=\"$CORE_DIR\"" \
+ $INCLUDES \
+ $(echo "$C_FILES" | sed 's/ /\n/g;s/\([a-zA-Z_0-9]*\)\n/src\/\1.c\n/g;s/\n/ /g') \
+ $LIBS
+
+echo "Installing onyx executable"
+sudo cp "../bin/onyx" "$BIN_DIR/onyx"
+
+C_FILES="onyxrun wasm_runtime "
+
+echo "Compiling onyx-run..."
+$CC -o "../bin/onyx-run" \
+ $FLAGS \
+ "-DCORE_INSTALLATION=\"CORE_DIR\"" \
+ $INCLUDES \
+ $(echo "$C_FILES" | sed 's/ /\n/g;s/\([a-zA-Z_0-9]*\)\n/src\/\1.c\n/g;s/\n/ /g') \
+ $LIBS
+
+echo "Installing onyx-run executable"
+sudo cp "../bin/onyx-run" "$BIN_DIR/onyx-run"
--- /dev/null
+#ifndef ONYXASTNODES_H
+#define ONYXASTNODES_H
+
+#include "stb_ds.h"
+#include "lex.h"
+#include "types.h"
+#include "errors.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(DirectiveInit) \
+ NODE(DirectiveLibrary) \
+ NODE(DirectiveRemove) \
+ \
+ 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(DistinctType) \
+ \
+ NODE(Binding) \
+ NODE(Alias) \
+ NODE(Injection) \
+ NODE(MemRes) \
+ NODE(Include) \
+ NODE(UsePackage) \
+ NODE(Global) \
+ NODE(Param) \
+ NODE(Function) \
+ NODE(OverloadedFunction) \
+ NODE(Interface) \
+ NODE(Constraint) \
+ \
+ NODE(PolyParam) \
+ NODE(PolySolution) \
+ NODE(SolidifiedFunction) \
+ NODE(PolyProc) \
+ NODE(PolyQuery) \
+ \
+ NODE(Note) \
+ NODE(CallSite) \
+ \
+ NODE(CodeBlock) \
+ NODE(DirectiveInsert) \
+ NODE(Macro) \
+ \
+ NODE(ForeignBlock) \
+ \
+ NODE(Package) \
+ \
+ NODE(ZeroValue)
+
+#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;
+ char* name;
+ Table(AstNode *) symbols;
+} Scope;
+
+
+typedef enum AstKind {
+ Ast_Kind_Error,
+ Ast_Kind_Package,
+ Ast_Kind_Load_File,
+ Ast_Kind_Load_Path,
+ Ast_Kind_Load_All,
+ Ast_Kind_Library_Path,
+ Ast_Kind_Memres,
+
+ Ast_Kind_Binding,
+ Ast_Kind_Alias,
+ Ast_Kind_Injection,
+ Ast_Kind_Function,
+ Ast_Kind_Overloaded_Function,
+ Ast_Kind_Polymorphic_Proc,
+ Ast_Kind_Polymorph_Query,
+ Ast_Kind_Interface,
+ Ast_Kind_Constraint,
+ Ast_Kind_Constraint_Sentinel,
+ 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_Distinct_Type,
+ 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_Directive_Init,
+ Ast_Kind_Directive_Library,
+ Ast_Kind_Directive_Remove,
+ Ast_Kind_Call_Site,
+
+ Ast_Kind_Code_Block,
+ Ast_Kind_Directive_Insert,
+ Ast_Kind_Macro,
+ Ast_Kind_Do_Block,
+
+ Ast_Kind_Foreign_Block,
+
+ Ast_Kind_Zero_Value,
+
+ 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_Const = BH_BIT(1),
+ Ast_Flag_Comptime = BH_BIT(2),
+ Ast_Flag_Private_Package = BH_BIT(3),
+ Ast_Flag_Private_File = BH_BIT(4),
+
+ // Function flags
+ Ast_Flag_Function_Used = BH_BIT(5),
+
+ // Expression flags
+ Ast_Flag_Expr_Ignored = BH_BIT(6),
+ Ast_Flag_Address_Taken = BH_BIT(7),
+
+ // Type flags
+ Ast_Flag_Type_Is_Resolved = BH_BIT(8),
+
+ Ast_Flag_No_Clone = BH_BIT(9),
+
+ Ast_Flag_Cannot_Take_Addr = BH_BIT(10),
+
+ // HACK: NullProcHack
+ Ast_Flag_Proc_Is_Null = BH_BIT(11),
+
+ Ast_Flag_From_Polymorphism = BH_BIT(12),
+
+ Ast_Flag_Incomplete_Body = BH_BIT(13),
+
+ Ast_Flag_Array_Literal_Typed = BH_BIT(14),
+
+ Ast_Flag_Has_Been_Symres = BH_BIT(15),
+
+ Ast_Flag_Has_Been_Checked = BH_BIT(16),
+
+ Ast_Flag_Static_If_Resolved = BH_BIT(17),
+
+ Ast_Flag_Symbol_Invisible = BH_BIT(18),
+
+ Ast_Flag_Header_Check_No_Error = BH_BIT(19),
+
+ Ast_Flag_Decl_Followed_By_Init = BH_BIT(20),
+
+ Ast_Flag_Param_Symbol_Dirty = BH_BIT(21),
+
+ Ast_Flag_Dead = BH_BIT(22),
+} 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_Subscript_Equals = 37,
+ Binary_Op_Ptr_Subscript = 38,
+
+ 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_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,
+
+ ONYX_INTRINSIC_ATOMIC_WAIT,
+ ONYX_INTRINSIC_ATOMIC_NOTIFY,
+
+ ONYX_INTRINSIC_ATOMIC_FENCE,
+
+ ONYX_INTRINSIC_ATOMIC_LOAD,
+ ONYX_INTRINSIC_ATOMIC_STORE,
+ ONYX_INTRINSIC_ATOMIC_ADD,
+ ONYX_INTRINSIC_ATOMIC_SUB,
+ ONYX_INTRINSIC_ATOMIC_AND,
+ ONYX_INTRINSIC_ATOMIC_OR,
+ ONYX_INTRINSIC_ATOMIC_XOR,
+ ONYX_INTRINSIC_ATOMIC_XCHG,
+ ONYX_INTRINSIC_ATOMIC_CMPXCHG,
+} 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 enum ConstraintCheckStatus {
+ Constraint_Check_Status_Queued,
+ Constraint_Check_Status_Failed,
+ Constraint_Check_Status_Success,
+} ConstraintCheckStatus;
+
+typedef struct ConstraintContext {
+ bh_arr(AstConstraint *) constraints;
+ ConstraintCheckStatus *constraint_checks;
+ b32 constraints_met : 1;
+ b32 produce_errors : 1;
+} ConstraintContext;
+
+
+typedef struct Arguments Arguments;
+struct Arguments {
+ bh_arr(AstTyped *) values;
+ bh_arr(AstNamedValue *) named_values;
+
+ // How many arguments were not baked.
+ i32 used_argument_count;
+};
+
+
+// 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 AstUnaryOp { AstTyped_base; UnaryOp operation; AstTyped *expr; };
+struct AstStrLit { AstTyped_base; u64 data_id; u64 length; b32 is_cstr: 1; };
+struct AstLocal { AstTyped_base; };
+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 AstNumLit {
+ AstTyped_base;
+ union {
+ i32 i;
+ i64 l;
+ f32 f;
+ f64 d;
+ } value;
+
+ b32 was_hex_literal : 1;
+};
+struct AstBinaryOp {
+ AstTyped_base;
+ BinaryOp operation;
+ AstTyped *left, *right;
+
+ Arguments *overload_args; // This is set of the binary operator is attempted to be overloaded
+ // but isnt successful yet.
+ AstBinaryOp *potential_substitute;
+};
+struct AstAddressOf {
+ AstTyped_base;
+ AstTyped *expr;
+
+ AstBinaryOp *potential_substitute;
+
+ // This is set by check_method_call.
+ // If set, the address of node can be removed if the
+ // type checking does not pass for it. This makes it
+ // possible to have something that will optionally
+ // have its address taken, if necessary.
+ b32 can_be_removed : 1;
+};
+struct AstArgument {
+ AstTyped_base;
+
+ AstTyped *value;
+
+ VarArgKind va_kind;
+ b32 is_baked : 1;
+ b32 pass_as_any : 1;
+};
+struct AstSubscript {
+ AstTyped_base;
+ BinaryOp __unused_operation; // This will be set to Binary_Op_Subscript
+ AstTyped *addr;
+ AstTyped *expr;
+
+ Arguments *overload_args; // This is set of the binary operator is attempted to be overloaded
+ // but isnt successful yet.
+ AstBinaryOp *potential_substitute;
+
+ 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;
+
+ AstTyped *filename_expr;
+ char *filename; // The parsed file name, with '\' sequences removed and resolved to a particular file if possible.
+
+ u32 data_id, 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 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 AstZeroValue {
+ AstTyped_base;
+};
+
+struct AstDirectiveSolidify {
+ AstTyped_base;
+
+ AstFunction* 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;
+
+ u32 statement_idx;
+};
+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;
+ b32 no_close : 1;
+};
+struct AstIfWhile {
+ AstNode_base;
+
+ Scope *scope;
+ AstNode* initialization;
+
+ AstTyped *cond;
+
+ AstBlock *true_stmt;
+ AstBlock *false_stmt;
+
+ union {
+ // Used by Static_If
+ struct {
+ Scope *defined_in_scope;
+ bh_arr(struct Entity *) true_entities;
+ bh_arr(struct Entity *) false_entities;
+ };
+
+ // Used by While
+ b32 bottom_test;
+ };
+};
+typedef struct AstIfWhile AstIf;
+typedef struct AstIfWhile AstWhile;
+
+typedef enum SwitchKind {
+ Switch_Kind_Integer,
+ Switch_Kind_Use_Equals,
+} SwitchKind;
+
+typedef struct CaseToBlock {
+ AstTyped *original_value;
+ AstBinaryOp *comparison;
+ AstBlock *block;
+} CaseToBlock;
+
+struct AstSwitchCase {
+ AstNode_base;
+
+ // NOTE: All expressions that end up in this block
+ bh_arr(AstTyped *) values;
+
+ AstBlock *block;
+
+ b32 is_default: 1; // Could this be inferred by the values array being null?
+};
+
+struct AstSwitch {
+ AstNode_base;
+
+ Scope *scope;
+ AstNode* initialization;
+
+ AstTyped *expr;
+
+ AstBlock *case_block;
+
+ bh_arr(AstSwitchCase *) cases;
+ AstBlock *default_case;
+
+ i32 yield_return_index;
+ SwitchKind switch_kind;
+
+ union {
+ struct {
+ // 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;
+ };
+
+ struct {
+ // NOTE: This is a mapping from the '==' binary op node to
+ // a pointer to the block that it is associated with.
+ bh_arr(CaseToBlock) case_exprs;
+ };
+ };
+};
+
+// 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; \
+ void* __unused; \
+ u64 type_id; \
+ Type* type
+struct AstType { AstType_base; };
+
+struct AstBasicType { AstType_base; Type* basic_type; };
+struct AstPointerType { AstType_base; AstType* elem; };
+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 AstFunctionType {
+ AstType_base;
+
+ // Used in a rare case in solve_for_polymorphic_param_type.
+ Type *partial_function_type;
+
+ AstType* return_type;
+
+ u64 param_count;
+ AstType* params[];
+};
+struct AstStructType {
+ AstType_base;
+ char *name;
+
+ bh_arr(AstStructMember *) members;
+ bh_arr(AstTyped *) meta_tags;
+
+ // u32 min_alignment, min_size;
+ AstTyped *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: This type is used when the structure has not been
+ // completely generated, but is a valid pointer to where the
+ // type will be generated to.
+ Type *pending_type;
+
+ // NOTE: Used to store statically bound expressions in the struct.
+ Scope* scope;
+
+ struct Entity* entity_type;
+ struct Entity* entity_defaults;
+
+ OnyxFilePos polymorphic_error_loc;
+ ConstraintContext constraints;
+
+ bh_arr(AstType *) polymorphic_argument_types;
+ bh_arr(AstPolySolution) polymorphic_arguments;
+
+ b32 pending_type_is_valid : 1;
+ b32 is_union : 1;
+ b32 is_packed : 1;
+ b32 ready_to_build_type : 1;
+};
+struct AstStructMember {
+ AstTyped_base;
+ AstTyped* initial_value;
+
+ bh_arr(AstTyped *) meta_tags;
+
+ b32 is_used : 1;
+};
+struct AstPolyStructParam {
+ AstTyped_base;
+};
+struct AstPolyStructType {
+ AstType_base;
+ char *name;
+
+ Scope *scope;
+ bh_arr(AstPolyStructParam) poly_params;
+ 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;
+
+ b32 is_flags : 1;
+};
+struct AstEnumValue { AstTyped_base; AstTyped* 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;
+};
+struct AstDistinctType {
+ AstType_base;
+ char *name;
+ AstType *base_type;
+ Type *dtcache;
+};
+
+// Top level nodes
+struct AstBinding { AstTyped_base; AstNode* node; };
+struct AstAlias { AstTyped_base; AstTyped* alias; };
+struct AstInclude { AstNode_base; AstTyped* name_node; char* name; };
+struct AstInjection {
+ AstTyped_base;
+ AstTyped* full_loc;
+ AstTyped* to_inject;
+ AstTyped* dest;
+ OnyxToken *symbol;
+};
+struct AstMemRes {
+ AstTyped_base;
+ AstTyped *initial_value;
+
+ struct Entity *type_entity;
+
+ b32 threadlocal : 1;
+
+ // Set and used in the wasm emission.
+ u32 data_id;
+ u32 tls_offset;
+};
+struct AstGlobal {
+ AstTyped_base;
+
+ char* 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 is_used : 1;
+ b32 use_processed : 1;
+ b32 is_baked : 1;
+};
+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;
+
+ b32 locked : 1;
+ b32 only_local_functions : 1;
+};
+
+// @CLEANUP: Is this really necessary?
+typedef struct InterfaceParam {
+ OnyxToken *value_token;
+ OnyxToken *type_token;
+} InterfaceParam;
+
+typedef struct InterfaceConstraint {
+ AstTyped *expr;
+ AstType *expected_type_expr;
+ Type *expected_type;
+
+ b32 invert_condition: 1;
+} InterfaceConstraint;
+
+struct AstInterface {
+ AstTyped_base;
+ char *name;
+
+ bh_arr(InterfaceParam) params;
+ bh_arr(InterfaceConstraint) exprs;
+};
+
+typedef enum ConstraintPhase {
+ Constraint_Phase_Waiting_To_Be_Queued = 0,
+ Constraint_Phase_Cloning_Expressions = 1,
+ Constraint_Phase_Checking_Expressions = 2,
+ Constraint_Phase_Finished = 3,
+} ConstraintPhase;
+
+struct AstConstraint {
+ AstNode_base;
+
+ ConstraintPhase phase;
+
+ AstInterface *interface;
+ bh_arr(AstType *) type_args;
+
+ ConstraintCheckStatus *report_status;
+
+ Scope* scope;
+ bh_arr(InterfaceConstraint) exprs;
+ u32 expr_idx;
+};
+
+
+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;
+
+typedef enum PolyProcLookupMethod {
+ PPLM_By_Arguments,
+ PPLM_By_Function_Type,
+} PolyProcLookupMethod;
+
+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;
+ struct Entity *func_header_entity;
+};
+
+struct AstFunction {
+ AstTyped_base;
+
+ Scope *scope;
+
+ bh_arr(AstParam) params;
+ AstType* return_type;
+
+ AstBlock *body;
+
+ char* 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;
+ Scope* poly_scope;
+
+ // 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;
+
+ OnyxToken* closing_brace;
+
+ 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;
+
+ ConstraintContext constraints;
+
+ bh_arr(AstTyped *) tags;
+
+ // Polymorphic procedures use the following fields
+ Scope *parent_scope_of_poly_proc;
+ bh_arr(AstPolyParam) poly_params;
+
+ bh_arr(AstPolySolution) known_slns;
+
+ Table(AstSolidifiedFunction) concrete_funcs;
+ bh_imap active_queries;
+
+ bh_arr(AstNode *) nodes_that_need_entities_after_clone;
+
+ b32 is_exported : 1;
+ b32 is_foreign : 1;
+ b32 is_intrinsic : 1;
+};
+
+struct AstPolyQuery {
+ AstNode_base;
+
+ AstFunction *proc;
+ PolyProcLookupMethod pp_lookup;
+ ptr given;
+ OnyxToken *error_loc;
+
+ bh_arr(AstPolySolution) slns;
+
+ AstFunction *function_header;
+
+ b32 error_on_fail : 1; // Whether or not to report errors on failing to match.
+ b32 successful_symres : 1; // If something successful happened in symbol resolution
+};
+
+
+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_name_expr;
+ AstTyped* export;
+};
+
+struct AstDirectiveDefined {
+ AstTyped_base;
+ AstTyped *expr;
+
+ b32 is_defined: 1;
+};
+
+struct AstDirectiveRemove {
+ AstNode_base;
+};
+
+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 {
+ AstTyped_base;
+
+ AstTyped *code_expr;
+};
+
+struct AstDirectiveInit {
+ AstNode_base;
+
+ AstTyped *init_proc;
+ bh_arr(AstDirectiveInit *) dependencies;
+};
+
+struct AstMacro {
+ AstTyped_base;
+
+ AstTyped* body;
+};
+
+struct AstDirectiveLibrary {
+ AstNode_base;
+
+ AstTyped *library_symbol; // This should resolve to a string literal
+ char *library_name;
+};
+
+struct AstForeignBlock {
+ AstTyped_base;
+
+ Scope* scope;
+ OnyxToken *module_name;
+ bh_arr(struct Entity *) captured_entities;
+
+ u32 foreign_block_number;
+};
+
+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_Failed,
+
+ 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_Enum_Value,
+ Entity_Type_Type_Alias,
+ Entity_Type_Memory_Reservation_Type,
+ Entity_Type_Use,
+ Entity_Type_Interface,
+ Entity_Type_Constraint_Check,
+ Entity_Type_Polymorphic_Proc,
+ Entity_Type_Polymorph_Query,
+ Entity_Type_Macro,
+ Entity_Type_Foreign_Block,
+ Entity_Type_Foreign_Function_Header,
+ Entity_Type_Temp_Function_Header, // Same as a Function_Header, except it disappears after it checks completely.
+ 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;
+ AstEnumValue *enum_value;
+ AstMemRes *mem_res;
+ AstFunction *poly_proc;
+ AstPolyQuery *poly_query;
+ AstForeignBlock *foreign_block;
+ AstMacro *macro;
+ AstUse *use;
+ AstInterface *interface;
+ AstConstraint *constraint;
+ AstDirectiveLibrary *library;
+ };
+} 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);
+void entity_change_type(EntityHeap* entities, Entity *ent, EntityType new_type);
+
+// 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);
+
+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_RUN,
+ 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_Onyx = 1,
+ Runtime_Wasi = 2,
+ Runtime_Js = 3,
+ Runtime_Custom = 4,
+};
+
+
+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;
+ b32 use_multi_threading : 1;
+ b32 generate_foreign_info : 1;
+
+ Runtime runtime;
+
+ bh_arr(const char *) included_folders;
+ bh_arr(const char *) files;
+ const char* target_file;
+ const char* documentation_file;
+
+ b32 debug_enabled;
+
+ i32 passthrough_argument_count;
+ char** passthrough_argument_data;
+};
+
+typedef struct Context Context;
+struct Context {
+ 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_almost_detected : 1;
+ 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 AstGlobal builtin_heap_start;
+extern AstGlobal builtin_stack_top;
+extern AstGlobal builtin_tls_base;
+extern AstGlobal builtin_tls_size;
+extern AstType *builtin_string_type;
+extern AstType *builtin_cstring_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 AstType *builtin_link_options_type;
+extern AstTyped *type_table_node;
+extern AstTyped *foreign_blocks_node;
+extern AstType *foreign_block_type;
+extern AstTyped *tagged_procedures_node;
+extern AstFunction *builtin_initialize_data_segments;
+extern AstFunction *builtin_run_init_procedures;
+extern bh_arr(AstFunction *) init_procedures;
+
+typedef struct BuiltinSymbol {
+ char* package;
+ char* sym;
+ AstNode* node;
+} BuiltinSymbol;
+
+extern const BuiltinSymbol builtin_symbols[];
+
+typedef struct IntrinsicMap {
+ char* name;
+ OnyxIntrinsic intrinsic;
+} IntrinsicMap;
+
+typedef Table(OnyxIntrinsic) IntrinsicTable;
+extern IntrinsicTable intrinsic_table;
+
+extern bh_arr(OverloadOption) operator_overloads[Binary_Op_Count];
+
+void initialize_builtins(bh_allocator a);
+void initalize_special_globals();
+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);
+
+typedef enum TypeMatch {
+ TYPE_MATCH_SUCCESS,
+ TYPE_MATCH_FAILED,
+ TYPE_MATCH_YIELD,
+ TYPE_MATCH_SPECIAL, // Only used for nest polymorph function lookups
+} TypeMatch;
+#define unify_node_and_type(node, type) (unify_node_and_type_((node), (type), 1))
+TypeMatch unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent);
+Type* resolve_expression_type(AstTyped* node);
+
+i64 get_expression_integer_value(AstTyped* node, b32 *out_is_valid);
+char *get_expression_string_value(AstTyped* node, b32 *out_is_valid);
+
+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);
+AstUnaryOp* make_cast(bh_allocator a, AstTyped* expr, Type* to);
+AstZeroValue* make_zero_value(bh_allocator a, OnyxToken *token, Type* type);
+
+void arguments_initialize(Arguments* args);
+b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg, b32 insert_zero_values);
+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);
+void arguments_clear_baked_flags(Arguments* args);
+TypeMatch check_arguments_against_type(Arguments* args, TypeFunction* func_type, VarArgKind* va_kind,
+ OnyxToken* location, char* func_name, struct OnyxError* error);
+i32 get_argument_buffer_size(TypeFunction* type, 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);
+
+void insert_poly_sln_into_scope(Scope* scope, AstPolySolution *sln);
+TypeMatch find_polymorphic_sln(AstPolySolution *out, AstPolyParam *param, AstFunction *func, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg);
+AstFunction* polymorphic_proc_lookup(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn);
+AstFunction* polymorphic_proc_solidify(AstFunction* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
+AstNode* polymorphic_proc_try_solidify(AstFunction* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
+AstFunction* polymorphic_proc_build_only_header(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual);
+AstFunction* polymorphic_proc_build_only_header_with_slns(AstFunction* pp, bh_arr(AstPolySolution) slns, b32 error_if_failed);
+b32 potentially_convert_function_to_polyproc(AstFunction *func);
+AstPolyCallType* convert_call_to_polycall(AstCall* call);
+
+void add_overload_option(bh_arr(OverloadOption)* poverloads, u64 precedence, AstTyped* overload);
+AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads, Arguments* args);
+AstTyped* find_matching_overload_by_type(bh_arr(OverloadOption) overloads, Type* type);
+void report_unable_to_match_overload(AstCall* call, bh_arr(OverloadOption) overloads);
+
+void expand_macro(AstCall** pcall, AstFunction* template);
+AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite, b32 error_if_failed);
+
+Type* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos, b32 error_if_failed);
+
+// NOTE: Useful inlined functions
+static inline b32 is_lval(AstNode* node) {
+ node = strip_aliases(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)
+ || (node->kind == Ast_Kind_Constraint_Sentinel)) // Bit of a hack, but this makes constraints like 'T->foo()' work.
+ 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 b32 node_is_addressable_literal(AstNode* node) {
+ return (node->kind == Ast_Kind_Struct_Literal)
+ || (node->kind == Ast_Kind_Array_Literal);
+}
+
+static inline Type* get_expression_type(AstTyped* expr) {
+ switch (expr->kind) {
+ case Ast_Kind_Block: case Ast_Kind_If: case Ast_Kind_While: return NULL;
+ default: return expr->type;
+ }
+}
+
+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 (AstFunction *) node;
+ if (node->kind == Ast_Kind_Macro) return get_function_from_node((AstNode*) ((AstMacro *) node)->body);
+ return NULL;
+}
+
+static inline void convert_polyproc_to_function(AstFunction *func) {
+ if (func->kind != Ast_Kind_Polymorphic_Proc) return;
+
+ func->kind = Ast_Kind_Function;
+ func->parent_scope_of_poly_proc = NULL;
+ func->poly_params = NULL;
+ func->known_slns = NULL;
+ func->concrete_funcs = NULL;
+ func->active_queries.hashes = NULL;
+ func->active_queries.entries = NULL;
+ func->poly_scope = NULL;
+ func->entity = NULL;
+ func->type = NULL;
+ func->tags = NULL;
+}
+
+static inline void convert_function_to_polyproc(AstFunction *func) {
+ if (func->kind != Ast_Kind_Function) return;
+
+ func->kind = Ast_Kind_Polymorphic_Proc;
+ func->parent_scope_of_poly_proc = func->scope->parent;
+ func->scope = NULL;
+ if (func->entity) entity_change_type(&context.entities, func->entity, Entity_Type_Polymorphic_Proc);
+}
+
+#endif // #ifndef ONYXASTNODES_H
--- /dev/null
+#ifndef ONYXDOC_H
+#define ONYXDOC_H
+
+#include "bh.h"
+#include "astnodes.h"
+
+typedef enum DocFormat {
+ Doc_Format_Human,
+ Doc_Format_Tags,
+ Doc_Format_Html,
+} DocFormat;
+
+typedef struct DocEntry {
+ OnyxFilePos pos;
+ char* sym; // Unused by doc generator
+ char* def;
+ char* additional;
+} DocEntry;
+
+typedef struct DocPackage {
+ const char* name;
+
+ bh_arr(DocEntry) public_entries;
+ bh_arr(DocEntry) private_entries;
+} DocPackage;
+
+typedef struct OnyxDocumentation {
+ bh_arena doc_arena;
+
+ DocFormat format;
+
+ bh_arr(DocPackage) package_docs;
+} OnyxDocumentation;
+
+OnyxDocumentation onyx_docs_generate();
+void onyx_docs_emit(OnyxDocumentation* doc, const char* filename);
+
+#endif
--- /dev/null
+#ifndef ONYXERRORS_H
+#define ONYXERRORS_H
+
+#include "bh.h"
+#include "lex.h"
+
+#include <stdarg.h>
+
+typedef enum OnyxErrorRank {
+ Error_Undefined = 0,
+ Error_Other = 1,
+ Error_Warning = 2,
+ Error_Waiting_On = 3,
+ Error_Critical = 4,
+} OnyxErrorRank;
+
+typedef struct OnyxError {
+ OnyxFilePos pos;
+ OnyxErrorRank rank;
+ 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_submit_error(OnyxError error);
+void onyx_report_error(OnyxFilePos pos, OnyxErrorRank rank, char * format, ...);
+void onyx_submit_warning(OnyxError error);
+void onyx_report_warning(OnyxFilePos pos, char* format, ...);
+void onyx_errors_print();
+b32 onyx_has_errors();
+void onyx_clear_errors();
+
+#endif
--- /dev/null
+#ifndef ONYXLEX_H
+#define ONYXLEX_H
+
+#include "bh.h"
+
+// NOTE: Used for global statistics
+extern u64 lexer_lines_processed;
+extern u64 lexer_tokens_processed;
+
+typedef enum TokenType {
+ Token_Type_Ascii_End = 256,
+ Token_Type_Unknown = 256,
+ Token_Type_End_Stream = 257,
+
+ Token_Type_Comment = 258,
+
+ Token_Type_Keyword_Package,
+ Token_Type_Keyword_Struct,
+ Token_Type_Keyword_Enum,
+ Token_Type_Keyword_Use,
+ Token_Type_Keyword_If,
+ Token_Type_Keyword_Else,
+ Token_Type_Keyword_Elseif,
+ Token_Type_Keyword_Return,
+ Token_Type_Keyword_Global,
+ Token_Type_Keyword_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_Keyword_Interface,
+ Token_Type_Keyword_Where,
+
+ 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);
+b32 token_same_file(OnyxToken *tkn1, OnyxToken *tkn2);
+
+#endif
--- /dev/null
+#ifndef ONYXPARSER_H
+#define ONYXPARSER_H
+
+#include "bh.h"
+
+#include "lex.h"
+#include "errors.h"
+#include "astnodes.h"
+
+typedef struct PolymorphicContext {
+ AstType* root_node;
+ bh_arr(AstPolyParam)* poly_params;
+} PolymorphicContext;
+
+typedef struct OnyxParser {
+ bh_allocator allocator;
+
+ Package *package;
+ Scope *file_scope;
+
+ // NOTE: not used since all tokens are lexed before parsing starts
+ OnyxTokenizer *tokenizer;
+ OnyxToken *prev;
+ OnyxToken *curr;
+
+ PolymorphicContext polymorph_context;
+
+ bh_arr(AstFunction *) current_function_stack;
+ Scope *current_scope;
+ bh_arr(bh_arr(Entity *) *) alternate_entity_placement_stack;
+ bh_arr(OnyxToken *) current_symbol_stack;
+
+ bh_arr(AstFlags) scope_flags;
+
+ bh_arr(AstTyped *) stored_tags;
+
+ u16 tag_depth : 16;
+
+ b32 hit_unexpected_token : 1;
+ b32 parse_calls : 1;
+} OnyxParser;
+
+const char* onyx_ast_node_kind_string(AstKind kind);
+void* onyx_ast_node_new(bh_allocator alloc, i32 size, AstKind kind);
+OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer);
+void onyx_parser_free(OnyxParser* parser);
+void onyx_parse(OnyxParser *parser);
+
+#endif // #ifndef ONYXPARSER_H
--- /dev/null
+#ifndef ONYXTYPES_H
+#define ONYXTYPES_H
+
+#include "bh.h"
+
+#define POINTER_SIZE 4
+
+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 OnyxToken* token;
+
+ struct AstTyped** initial_value;
+ i32 use_through_pointer_index;
+ b32 included_through_use : 1;
+ b32 used : 1;
+
+ bh_arr(struct AstTyped *) meta_tags;
+} StructMember;
+
+typedef struct TypeWithOffset TypeWithOffset;
+struct TypeWithOffset {
+ Type* type;
+ u32 offset;
+};
+
+typedef enum StructProcessingStatus {
+ SPS_Start,
+ SPS_Members_Done,
+ SPS_Uses_Done,
+} StructProcessingStatus;
+
+#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; \
+ Table(StructMember *) members; \
+ bh_arr(StructMember *) memarr; \
+ bh_arr(struct AstPolySolution) poly_sln; \
+ bh_arr(TypeWithOffset) linear_members; \
+ struct AstType *constructed_from; \
+ bh_arr(struct AstTyped *) meta_tags; \
+ StructProcessingStatus status; \
+ }) \
+ TYPE_KIND(PolyStruct, struct { \
+ char* name; \
+ bh_arr(struct AstTyped *) meta_tags; \
+ }) \
+ TYPE_KIND(Compound, struct { \
+ u32 count; \
+ u32 size; \
+ bh_arr(TypeWithOffset) linear_members; \
+ Type* types[]; \
+ }) \
+ TYPE_KIND(Array, struct { Type* elem; u32 size; u32 count; }) \
+ TYPE_KIND(Slice, struct { Type *elem; }) \
+ TYPE_KIND(DynArray, struct { Type *elem; }) \
+ TYPE_KIND(VarArgs, struct { Type *elem; }) \
+ TYPE_KIND(Enum, struct { \
+ char* name; \
+ Type* backing; \
+ b32 is_flags; \
+ }) \
+ TYPE_KIND(Distinct, struct { \
+ char* name; \
+ Type* base_type; \
+ })
+
+
+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;
+struct AstStructLiteral;
+
+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_implicit_type_of_struct_literal(bh_allocator alloc, struct AstStructLiteral* lit);
+
+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);
+b32 type_struct_member_apply_use(bh_allocator alloc, Type *s_type, StructMember *smem);
+
+const char* type_get_unique_name(Type* type);
+const char* type_get_name(Type* type);
+u32 type_get_alignment_log2(Type* type);
+Type* type_get_contained_type(Type* type);
+
+b32 type_lookup_member(Type* type, char* member, StructMember* smem);
+b32 type_lookup_member_by_idx(Type* type, i32 idx, StructMember* smem);
+
+i32 type_linear_member_count(Type* type);
+b32 type_linear_member_lookup(Type* type, i32 idx, TypeWithOffset* two);
+i32 type_get_idx_of_linear_member_with_offset(Type* type, u32 offset);
+
+b32 type_struct_is_simple(Type* type);
+
+b32 type_is_pointer(Type* type);
+b32 type_is_rawptr(Type* type);
+b32 type_is_array(Type* tyoe);
+b32 type_is_struct(Type* type);
+b32 type_is_bool(Type* type);
+b32 type_is_small_integer(Type* type);
+b32 type_is_integer(Type* type);
+b32 type_is_numeric(Type* type);
+b32 type_is_compound(Type* type);
+b32 type_is_simd(Type* type);
+b32 type_results_in_void(Type* type);
+b32 type_is_array_accessible(Type* type);
+b32 type_is_structlike(Type* type);
+b32 type_is_structlike_strict(Type* type);
+u32 type_structlike_mem_count(Type* type);
+u32 type_structlike_is_simple(Type* type);
+b32 type_is_sl_constructable(Type* type);
+b32 type_struct_constructed_from_poly_struct(Type* struct_type, struct AstType* from);
+
+#endif // #ifndef ONYX_TYPES
--- /dev/null
+#include "bh.h"
+
+#include "astnodes.h"
+
+extern bh_scratch global_scratch;
+extern bh_allocator global_scratch_allocator;
+
+extern bh_managed_heap global_heap;
+extern bh_allocator global_heap_allocator;
+
+const char* onyx_ast_node_kind_string(AstKind kind);
+
+Package* package_lookup(char* package_name);
+Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc, OnyxFilePos pos);
+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);
+AstNode* try_symbol_raw_resolve_from_type(Type *type, char* symbol);
+Scope *get_scope_from_node(AstNode *node);
+Scope *get_scope_from_node_or_create(AstNode *node);
+
+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);
+
+u32 levenshtein_distance(const char *str1, const char *str2);
+char *find_closest_symbol_in_scope_and_parents(Scope *scope, char *sym);
+char *find_closest_symbol_in_node(AstNode *node, char *sym);
+
+extern AstTyped node_that_signals_a_yield;
+extern AstTyped node_that_signals_failure;
--- /dev/null
+#ifndef ONYXWASM_H
+#define ONYXWASM_H
+
+#include "bh.h"
+
+#include "astnodes.h"
+#include "errors.h"
+
+typedef u8 WasmType;
+
+typedef struct WasmFuncType {
+ // NOTE: For now, WASM only allows for 1 return value.
+ // This may be lifted in the future.
+ i32 param_count;
+ WasmType return_type;
+ WasmType param_types[];
+} WasmFuncType;
+
+#define SIMD_INSTR_MASK 0x10000
+#define EXT_INSTR_MASK 0x20000
+#define ATOMIC_INSTR_MASK 0x40000
+
+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_INIT = EXT_INSTR_MASK | 0x08,
+ WI_MEMORY_COPY = EXT_INSTR_MASK | 0x0a,
+ WI_MEMORY_FILL = EXT_INSTR_MASK | 0x0b,
+
+ WI_ATOMIC_NOTIFY = ATOMIC_INSTR_MASK | 0x00,
+ WI_ATOMIC_WAIT32 = ATOMIC_INSTR_MASK | 0x01,
+ WI_ATOMIC_WAIT64 = ATOMIC_INSTR_MASK | 0x02,
+
+ WI_ATOMIC_FENCE = ATOMIC_INSTR_MASK | 0x03,
+
+ WI_ATOMIC_I32_LOAD = ATOMIC_INSTR_MASK | 0x10,
+ WI_ATOMIC_I64_LOAD = ATOMIC_INSTR_MASK | 0x11,
+ WI_ATOMIC_I32_LOAD8_U = ATOMIC_INSTR_MASK | 0x12,
+ WI_ATOMIC_I32_LOAD16_U = ATOMIC_INSTR_MASK | 0x13,
+ WI_ATOMIC_I64_LOAD8_U = ATOMIC_INSTR_MASK | 0x14,
+ WI_ATOMIC_I64_LOAD16_U = ATOMIC_INSTR_MASK | 0x15,
+ WI_ATOMIC_I64_LOAD32_U = ATOMIC_INSTR_MASK | 0x16,
+
+ WI_ATOMIC_I32_STORE = ATOMIC_INSTR_MASK | 0x17,
+ WI_ATOMIC_I64_STORE = ATOMIC_INSTR_MASK | 0x18,
+ WI_ATOMIC_I32_STORE8 = ATOMIC_INSTR_MASK | 0x19,
+ WI_ATOMIC_I32_STORE16 = ATOMIC_INSTR_MASK | 0x1a,
+ WI_ATOMIC_I64_STORE8 = ATOMIC_INSTR_MASK | 0x1b,
+ WI_ATOMIC_I64_STORE16 = ATOMIC_INSTR_MASK | 0x1c,
+ WI_ATOMIC_I64_STORE32 = ATOMIC_INSTR_MASK | 0x1d,
+
+ WI_ATOMIC_I32_ADD = ATOMIC_INSTR_MASK | 0x1e,
+ WI_ATOMIC_I64_ADD = ATOMIC_INSTR_MASK | 0x1f,
+ WI_ATOMIC_I32_ADD8_U = ATOMIC_INSTR_MASK | 0x20,
+ WI_ATOMIC_I32_ADD16_U = ATOMIC_INSTR_MASK | 0x21,
+ WI_ATOMIC_I64_ADD8_U = ATOMIC_INSTR_MASK | 0x22,
+ WI_ATOMIC_I64_ADD16_U = ATOMIC_INSTR_MASK | 0x23,
+ WI_ATOMIC_I64_ADD32_U = ATOMIC_INSTR_MASK | 0x24,
+
+ WI_ATOMIC_I32_SUB = ATOMIC_INSTR_MASK | 0x25,
+ WI_ATOMIC_I64_SUB = ATOMIC_INSTR_MASK | 0x26,
+ WI_ATOMIC_I32_SUB8_U = ATOMIC_INSTR_MASK | 0x27,
+ WI_ATOMIC_I32_SUB16_U = ATOMIC_INSTR_MASK | 0x28,
+ WI_ATOMIC_I64_SUB8_U = ATOMIC_INSTR_MASK | 0x29,
+ WI_ATOMIC_I64_SUB16_U = ATOMIC_INSTR_MASK | 0x2a,
+ WI_ATOMIC_I64_SUB32_U = ATOMIC_INSTR_MASK | 0x2b,
+
+ WI_ATOMIC_I32_AND = ATOMIC_INSTR_MASK | 0x2c,
+ WI_ATOMIC_I64_AND = ATOMIC_INSTR_MASK | 0x2d,
+ WI_ATOMIC_I32_AND8_U = ATOMIC_INSTR_MASK | 0x2e,
+ WI_ATOMIC_I32_AND16_U = ATOMIC_INSTR_MASK | 0x2f,
+ WI_ATOMIC_I64_AND8_U = ATOMIC_INSTR_MASK | 0x30,
+ WI_ATOMIC_I64_AND16_U = ATOMIC_INSTR_MASK | 0x31,
+ WI_ATOMIC_I64_AND32_U = ATOMIC_INSTR_MASK | 0x32,
+
+ WI_ATOMIC_I32_OR = ATOMIC_INSTR_MASK | 0x33,
+ WI_ATOMIC_I64_OR = ATOMIC_INSTR_MASK | 0x34,
+ WI_ATOMIC_I32_OR8_U = ATOMIC_INSTR_MASK | 0x35,
+ WI_ATOMIC_I32_OR16_U = ATOMIC_INSTR_MASK | 0x36,
+ WI_ATOMIC_I64_OR8_U = ATOMIC_INSTR_MASK | 0x37,
+ WI_ATOMIC_I64_OR16_U = ATOMIC_INSTR_MASK | 0x38,
+ WI_ATOMIC_I64_OR32_U = ATOMIC_INSTR_MASK | 0x39,
+
+ WI_ATOMIC_I32_XOR = ATOMIC_INSTR_MASK | 0x3a,
+ WI_ATOMIC_I64_XOR = ATOMIC_INSTR_MASK | 0x3b,
+ WI_ATOMIC_I32_XOR8_U = ATOMIC_INSTR_MASK | 0x3c,
+ WI_ATOMIC_I32_XOR16_U = ATOMIC_INSTR_MASK | 0x3d,
+ WI_ATOMIC_I64_XOR8_U = ATOMIC_INSTR_MASK | 0x3e,
+ WI_ATOMIC_I64_XOR16_U = ATOMIC_INSTR_MASK | 0x3f,
+ WI_ATOMIC_I64_XOR32_U = ATOMIC_INSTR_MASK | 0x40,
+
+ WI_ATOMIC_I32_XCHG = ATOMIC_INSTR_MASK | 0x41,
+ WI_ATOMIC_I64_XCHG = ATOMIC_INSTR_MASK | 0x42,
+ WI_ATOMIC_I32_XCHG8_U = ATOMIC_INSTR_MASK | 0x43,
+ WI_ATOMIC_I32_XCHG16_U = ATOMIC_INSTR_MASK | 0x44,
+ WI_ATOMIC_I64_XCHG8_U = ATOMIC_INSTR_MASK | 0x45,
+ WI_ATOMIC_I64_XCHG16_U = ATOMIC_INSTR_MASK | 0x46,
+ WI_ATOMIC_I64_XCHG32_U = ATOMIC_INSTR_MASK | 0x47,
+
+ WI_ATOMIC_I32_CMPXCHG = ATOMIC_INSTR_MASK | 0x48,
+ WI_ATOMIC_I64_CMPXCHG = ATOMIC_INSTR_MASK | 0x49,
+ WI_ATOMIC_I32_CMPXCHG8_U = ATOMIC_INSTR_MASK | 0x4a,
+ WI_ATOMIC_I32_CMPXCHG16_U = ATOMIC_INSTR_MASK | 0x4b,
+ WI_ATOMIC_I64_CMPXCHG8_U = ATOMIC_INSTR_MASK | 0x4c,
+ WI_ATOMIC_I64_CMPXCHG16_U = ATOMIC_INSTR_MASK | 0x4d,
+ WI_ATOMIC_I64_CMPXCHG32_U = ATOMIC_INSTR_MASK | 0x4e,
+} 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;
+ OnyxToken *location;
+} 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;
+ union {
+ i32 idx;
+ struct {
+ i32 min, max;
+ b32 shared;
+ };
+ };
+ char *mod, *name;
+} WasmImport;
+
+typedef struct WasmDatum {
+ u32 id;
+ u32 offset_, alignment;
+ u32 length;
+ ptr data;
+} WasmDatum;
+
+typedef enum DatumPatchInfoKind {
+ Datum_Patch_Instruction,
+ Datum_Patch_Data,
+ Datum_Patch_Relative,
+} DatumPatchInfoKind;
+
+//
+// This represents a pointer that should be filled in
+// later when the corresponding data element is placed.
+//
+// There are three kinds of patches:
+// - Instruction
+// - Data
+// - Relative
+//
+// In all cases, the `data_id` member is set to the id
+// of the WasmDatum entry that will be the base address,
+// and then the `offset` member will be added to that.
+//
+// In instruction patches, the `index` member is set
+// to the index of the function where the instruction should
+// be patched. The `location` member is set to the instruction
+// that needs to have its data changed.
+//
+// In data patches, the `index` member is set to the id
+// of the WasmDatum entry that needs to have a part of it
+// updated. The `location` member is the offset into the
+// data to update. It is assumed that 4 bytes will be reserved
+// to be replaced with the pointer value.
+//
+// In relative patches, `index` member is set to the id
+// of the WasmDatum entry that needs to have a part of it
+// updated. The `location` member is the offset into the
+// data to update. The difference between `Data` and `Relative`
+// is that `Relative` *adds* the base address to the current
+// value in the 4 bytes, as opposed to replacing it. As a
+// convenience, if the value is 0 (null), it will remain as
+// 0.
+//
+typedef struct DatumPatchInfo {
+ DatumPatchInfoKind kind;
+ u32 data_id;
+ u32 offset;
+ u32 location;
+ u32 index;
+} DatumPatchInfo;
+
+// Context used when building a constexpr buffer
+typedef struct ConstExprContext {
+ struct OnyxWasmModule *module;
+ ptr data;
+ u32 data_id;
+} ConstExprContext;
+
+typedef enum DeferredStmtType {
+ Deferred_Stmt_Node,
+ Deferred_Stmt_Code,
+} DeferredStmtType;
+
+typedef struct DeferredStmt {
+ DeferredStmtType type;
+ u32 depth;
+ AstDefer *defer_node;
+
+ union {
+ AstNode *stmt;
+ struct {
+ WasmInstruction* instructions;
+ u32 instruction_count;
+ };
+ };
+} DeferredStmt;
+
+typedef struct AllocatedSpace {
+ u64 depth;
+ AstTyped *expr;
+} AllocatedSpace;
+
+typedef struct StrLitInfo {
+ u32 data_id;
+ u32 len;
+} StrLitInfo;
+
+typedef struct PatchInfo {
+ u32 instruction_index;
+} PatchInfo;
+
+typedef struct ForRemoveInfo {
+ // These are WASM locals
+ u64 iterator_remove_func;
+ u64 iterator_data_ptr;
+
+ i32 remove_func_type_idx;
+} ForRemoveInfo;
+
+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;
+
+ i32 current_func_idx;
+ 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;
+ bh_arr(DatumPatchInfo) data_patches;
+
+ bh_arr(ForRemoveInfo) for_remove_info;
+
+ bh_arr(AstForeignBlock *) foreign_blocks;
+ u32 next_foreign_block_idx;
+
+ bh_arr(AstFunction *) procedures_with_tags;
+
+ // 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.
+ Table(i32) type_map;
+
+ Table(StrLitInfo) loaded_file_info;
+ 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;
+ Table(WasmExport) exports;
+ bh_arr(WasmGlobal) globals;
+ bh_arr(WasmFunc) funcs;
+ bh_arr(WasmDatum) data;
+ bh_arr(i32) elems;
+ bh_arr(char *) libraries;
+ bh_arr(char *) library_paths;
+ b32 needs_memory_section;
+ u32 memory_min_size;
+ u32 memory_max_size;
+
+ // 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_tls_offset;
+ u32 next_elem_idx;
+ u32 foreign_function_count;
+
+ i32 *stack_top_ptr;
+ i32 *tls_size_ptr;
+ i32 *heap_start_ptr;
+ u64 stack_base_idx;
+ CallingConvention curr_cc;
+ i32 null_proc_func_idx;
+
+ b32 has_stack_locals : 1;
+
+#ifdef ENABLE_DEBUG_INFO
+ struct DebugContext *debug_context;
+#endif
+
+} OnyxWasmModule;
+
+typedef struct OnyxWasmLinkOptions {
+ b32 stack_first;
+ u32 stack_size;
+ u32 stack_alignment;
+
+ u32 null_reserve_size;
+
+ b32 import_memory;
+ char *import_memory_module_name;
+ char *import_memory_import_name;
+
+ b32 export_memory;
+ char *export_memory_name;
+
+ b32 export_func_table;
+ char *export_func_table_name;
+
+ u32 memory_min_size;
+ u32 memory_max_size;
+} OnyxWasmLinkOptions;
+
+b32 onyx_wasm_build_link_options_from_node(OnyxWasmLinkOptions *opts, struct AstTyped *node);
+
+OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc);
+void onyx_wasm_module_link(OnyxWasmModule *module, OnyxWasmLinkOptions *options);
+void onyx_wasm_module_free(OnyxWasmModule* module);
+void onyx_wasm_module_write_to_buffer(OnyxWasmModule* module, bh_buffer* buffer);
+void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file);
+
+#ifdef ENABLE_RUN_WITH_WASMER
+void onyx_run_initialize(b32 debug_enabled);
+b32 onyx_run_wasm(bh_buffer code_buffer, int argc, char *argv[]);
+#endif
+
+#ifdef ENABLE_DEBUG_INFO
+
+typedef enum DebugOpType {
+ DOT_END = 0b00000000,
+ DOT_SET = 0b00000001,
+ DOT_PUSHF = 0b00000010,
+ DOT_POPF = 0b00000011,
+ DOT_SYM = 0b00000100,
+
+ DOT_INC = 0b01000000,
+ DOT_DEC = 0b10000000,
+ DOT_REP = 0b11000000,
+} DebugOpType;
+
+typedef struct DebugFuncContext {
+ u32 func_index;
+ u32 file_id;
+ u32 line;
+ u32 op_offset;
+ u32 stack_ptr_idx;
+
+ u32 name_length;
+ char *name;
+} DebugFuncContext;
+
+typedef struct DebugFileInfo {
+ u32 file_id;
+ u32 line_count;
+} DebugFileInfo;
+
+typedef enum DebugSymLoc {
+ DSL_REGISTER = 1,
+ DSL_STACK = 2,
+ DSL_GLOBAL = 3,
+} DebugSymLoc;
+
+typedef struct DebugSymInfo {
+ u32 sym_id;
+ u32 location_type;
+ u64 location_num;
+ char *name;
+ u32 type;
+} DebugSymInfo;
+
+typedef struct DebugSymPatch {
+ u32 func_idx;
+ u32 sym_id;
+ u64 local_idx;
+} DebugSymPatch;
+
+typedef struct DebugContext {
+ bh_allocator allocator;
+
+ Table(DebugFileInfo) file_info;
+ u32 next_file_id;
+
+ bh_arr(DebugSymInfo) sym_info;
+ bh_arr(DebugSymPatch) sym_patches;
+ u32 next_sym_id;
+
+ bh_arr(DebugFuncContext) funcs;
+ bh_buffer op_buffer;
+
+ // Used during building the debug info
+ OnyxToken *last_token;
+ b32 last_op_was_rep : 1;
+} DebugContext;
+
+#endif
+
+#endif
--- /dev/null
+#include "astnodes.h"
+#include "parser.h"
+#include "utils.h"
+
+static const char* ast_node_names[] = {
+ "ERROR",
+ "PACKAGE",
+ "INCLUDE FILE",
+ "INCLUDE FOLDER",
+ "INCLUDE ALL IN FOLDER",
+ "INCLUDE LIBRARY PATH",
+ "MEMORY RESERVATION",
+
+ "BINDING",
+ "ALIAS",
+ "INJECTION",
+ "FUNCTION",
+ "OVERLOADED_FUNCTION",
+ "POLYMORPHIC PROC",
+ "POLYMORPH QUERY",
+ "INTERFACE",
+ "CONSTRAINT",
+ "CONSTRAINT SENTITEL",
+ "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",
+ "DISTINCT TYPE",
+ "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",
+ "CASE",
+
+ "SOLIDIFY",
+ "STATIC IF",
+ "STATIC ERROR",
+ "ADD OVERLOAD",
+ "OPERATOR OVERLOAD",
+ "EXPORT",
+ "DEFINED",
+ "TAG",
+ "INIT",
+ "LIBRARY",
+ "REMOVE",
+ "CALL SITE",
+
+ "CODE BLOCK",
+ "DIRECTIVE INSERT",
+ "MACRO",
+ "DO BLOCK",
+
+ "FOREIGN BLOCK",
+ "ZERO VALUE",
+
+ "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",
+ "Failed",
+};
+
+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",
+ "Enum Value",
+ "Type Alias",
+ "Memory Reservation Type",
+ "Use",
+ "Interface",
+ "Constraint Check",
+ "Polymorphic Proc",
+ "Polymorph Query",
+ "Foreign Block",
+ "Macro",
+ "Foreign_Function Header",
+ "Temporary Function Header",
+ "Function Header",
+ "Global Header",
+ "Process Directive",
+ "Struct Member Default",
+ "Memory Reservation",
+ "Expression",
+ "Global",
+ "Overloaded_Function",
+ "Function",
+};
+
+AstNumLit* ast_reduce_type_compare(bh_allocator a, AstBinaryOp* node) {
+ AstType* left = (AstType *) ast_reduce(a, node->left);
+ AstType* right = (AstType *) ast_reduce(a, node->right);
+
+ Type* left_type = type_build_from_ast(context.ast_alloc, left);
+ Type* right_type = type_build_from_ast(context.ast_alloc, right);
+
+ 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 = (AstType *) &basic_type_bool;
+ res->type = &basic_types[Basic_Kind_Bool];
+ res->next = node->next;
+
+ switch (node->operation) {
+ case Binary_Op_Equal: res->value.l = left_type->id == right_type->id; break;
+ case Binary_Op_Not_Equal: res->value.l = left_type->id != right_type->id; break;
+ default: assert(("Bad case in ast_reduce_type_compare", 0));
+ }
+
+ return res;
+}
+
+#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 (node_is_type((AstNode *) left) && node_is_type((AstNode *) right)) {
+ if (node->operation == Binary_Op_Equal || node->operation == Binary_Op_Not_Equal) {
+ return (AstNumLit *) ast_reduce_type_compare(a, node);
+ }
+ }
+
+ 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* to_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, to_type)) return 1;
+ if (!type_is_numeric(to_type)) return 0;
+
+ Type *type = to_type;
+ if (type->kind == Type_Kind_Enum) type = type->Enum.backing;
+ if (type->kind == Type_Kind_Distinct) type = type->Distinct.base_type;
+
+ 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 = to_type;
+ return 1;
+ }
+ switch (type->Basic.size) {
+ case 1: if (value <= 255) {
+ num->type = to_type;
+ return 1;
+ }
+ case 2: if (value <= 65535) {
+ num->type = to_type;
+ return 1;
+ }
+ case 4: if (value <= 4294967295) {
+ num->type = to_type;
+ return 1;
+ }
+ }
+
+ onyx_report_error(num->token->pos, Error_Critical, "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 = to_type;
+ return 1;
+ } break;
+ case 2: if (-32768ll <= value && value <= 32767ll) {
+ num->value.i = (i32) value;
+ num->type = to_type;
+ return 1;
+ } break;
+ case 4: if (-2147483648ll <= value && value <= 2147483647ll) {
+ num->value.i = (i32) value;
+ num->type = to_type;
+ return 1;
+ } break;
+ case 8: { num->type = to_type;
+ return 1;
+ } break;
+ }
+
+ onyx_report_error(num->token->pos, Error_Critical, "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, Error_Critical, "Integer '%l' does not fit in 32-bit float exactly.", num->value.l);
+ return 0;
+ }
+
+ num->type = to_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, Error_Critical, "Integer '%l' does not fit in 64-bit float exactly.", num->value.l);
+ return 0;
+ }
+
+ num->type = to_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 = to_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 = to_type;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+// TODO: This function should be able return a "yield" condition. There
+// are a couple cases that need to yield in order to be correct, like
+// polymorphic functions with a typeof for the return type.
+TypeMatch unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent) {
+ AstTyped* node = *pnode;
+ if (type == NULL) return TYPE_MATCH_FAILED;
+ if (node == NULL) return TYPE_MATCH_FAILED;
+
+ if (node->kind == Ast_Kind_Struct_Literal && (node->type_node == NULL && node->type == NULL)) {
+ if (node->entity != NULL) return TYPE_MATCH_SUCCESS;
+ if (type->kind == Type_Kind_VarArgs) type = type->VarArgs.elem;
+
+ //
+ // If the structure literal has arguments, and the type is not constructable
+ // using a struct literal, then they cannot be unified. However, if no arguments
+ // are given, e.g. .{}, then any type should be matched, as that is the universal
+ // zero-value.
+ if (!type_is_sl_constructable(type)) {
+ AstStructLiteral *sl = (AstStructLiteral *) node;
+ if (bh_arr_length(sl->args.values) != 0 || bh_arr_length(sl->args.named_values) != 0) {
+ return TYPE_MATCH_FAILED;
+ }
+ }
+
+ // If this shouldn't make permanent changes and submit entities,
+ // just assume that it works and don't submit the entities.
+ if (!permanent) return TYPE_MATCH_SUCCESS;
+
+ node->type = type;
+
+ add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
+ return TYPE_MATCH_SUCCESS;
+ }
+
+ if (node->kind == Ast_Kind_Array_Literal && node->type == NULL) {
+ if (node->entity != NULL) return TYPE_MATCH_SUCCESS;
+
+ // If this shouldn't make permanent changes and submit entities,
+ // just assume that it works and don't submit the entities.
+ if (!permanent) return TYPE_MATCH_SUCCESS;
+
+ Type* array_type=NULL;
+ switch (type->kind) {
+ case Type_Kind_Array: array_type = type; break;
+ case Type_Kind_Slice: {
+ Type* elem_type = type->Slice.elem;
+ AstArrayLiteral* al = (AstArrayLiteral *) node;
+ array_type = type_make_array(context.ast_alloc, elem_type, bh_arr_length(al->values));
+
+ *pnode = (AstTyped *) make_cast(context.ast_alloc, node, type);
+
+ break;
+ }
+
+ default: assert(0);
+ }
+
+ node->type = array_type;
+ node->flags |= Ast_Flag_Array_Literal_Typed;
+
+ add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
+ return TYPE_MATCH_SUCCESS;
+ }
+
+ 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) {
+ if (context.cycle_detected) {
+ token_toggle_end(node->token);
+ char *closest = find_closest_symbol_in_node((AstNode *) ast_type, node->token->text);
+ token_toggle_end(node->token);
+
+ if (closest) {
+ onyx_report_error(node->token->pos, Error_Critical, "'%b' does not exist in '%s'. Did you mean '%s'?",
+ node->token->text, node->token->length,
+ type_get_name(type),
+ closest);
+ return TYPE_MATCH_FAILED;
+ }
+ }
+
+ return TYPE_MATCH_YIELD;
+ }
+
+ if (permanent) *pnode = (AstTyped *) resolved;
+ return TYPE_MATCH_SUCCESS;
+ }
+
+ if (node->kind == Ast_Kind_Overloaded_Function) {
+ AstTyped* func = find_matching_overload_by_type(((AstOverloadedFunction *) node)->overloads, type);
+ if (func == NULL) return TYPE_MATCH_FAILED;
+ if (func == (AstTyped *) &node_that_signals_a_yield) return TYPE_MATCH_YIELD;
+
+ // 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((AstFunction *) node, PPLM_By_Function_Type, type, node->token);
+ if (func == NULL) return TYPE_MATCH_FAILED;
+ if (func == (AstFunction *) &node_that_signals_a_yield) return TYPE_MATCH_YIELD;
+
+ *pnode = (AstTyped *) func;
+ node = *pnode;
+ }
+
+ // HACK: NullProcHack
+ // The null_proc matches any procedure, and because of that, will cause a runtime error if you
+ // try to call it.
+ if (type->kind == Type_Kind_Function && (node->flags & Ast_Flag_Proc_Is_Null) != 0) return TYPE_MATCH_SUCCESS;
+
+ // The normal case where everything works perfectly.
+ Type* node_type = get_expression_type(node);
+ if (types_are_compatible(node_type, type)) return TYPE_MATCH_SUCCESS;
+
+ Type* any_type = type_build_from_ast(context.ast_alloc, builtin_any_type);
+ if (any_type == NULL) return TYPE_MATCH_YIELD;
+ i64 any_id = any_type->id;
+
+ if (node_type && node_type->id != any_id && type->id == any_id) return TYPE_MATCH_SUCCESS;
+
+ // Here are some of the ways you can unify a node with a type if the type of the
+ // node does not match the given type:
+ //
+ // If the nodes type is a function type and that function has an automatic return
+ // value placeholder, fill in that placeholder with the actual type.
+ // :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 TYPE_MATCH_SUCCESS;
+ }
+
+ // If the node is an auto cast (~~) node, then check to see if the cast is legal
+ // to the destination type, and if it is change the type to cast to.
+ if (node_is_auto_cast((AstNode *) node)) {
+ char* dummy;
+ Type* from_type = get_expression_type(((AstUnaryOp *) node)->expr);
+ if (!from_type || !cast_is_legal(from_type, type, &dummy)) {
+ return TYPE_MATCH_FAILED;
+
+ } else {
+ if (permanent) ((AstUnaryOp *) node)->type = type;
+ return TYPE_MATCH_SUCCESS;
+ }
+ }
+
+ // If the destination type is a slice, then automatically convert arrays, dynamic
+ // arrays, and var args, if they are the same type. This is big convenience feature
+ // that makes working with arrays much easier.
+ // [N] T -> [] T
+ // [..] T -> [] T
+ // ..T -> [] T
+ else if (node_type && type->kind == Type_Kind_Slice) {
+ if (node_type->kind == Type_Kind_Array || node_type->kind == Type_Kind_DynArray || node_type->kind == Type_Kind_VarArgs) {
+ char* dummy;
+ b32 legal = cast_is_legal(node_type, type, &dummy);
+ if (permanent && legal) {
+ *pnode = (AstTyped *) make_cast(context.ast_alloc, node, type);
+ }
+
+ return legal ? TYPE_MATCH_SUCCESS : TYPE_MATCH_FAILED;
+ }
+ }
+
+ // If the node is a numeric literal, try to convert it to the destination type.
+ else if (node->kind == Ast_Kind_NumLit) {
+ if (convert_numlit_to_type((AstNumLit *) node, type)) return TYPE_MATCH_SUCCESS;
+ }
+
+ // If the node is a compound expression, and it doesn't have a type created,
+ // recursive call this function with the individual components of the compound
+ // expression.
+ else if (node->kind == Ast_Kind_Compound) {
+ if (type->kind != Type_Kind_Compound) return TYPE_MATCH_FAILED;
+
+ AstCompound* compound = (AstCompound *) node;
+
+ u32 expr_count = bh_arr_length(compound->exprs);
+ if (expr_count != type->Compound.count) return TYPE_MATCH_FAILED;
+
+ fori (i, 0, (i64) expr_count) {
+ TypeMatch tm = unify_node_and_type_(&compound->exprs[i], type->Compound.types[i], permanent);
+ if (tm != TYPE_MATCH_SUCCESS) {
+ return tm;
+ }
+ }
+
+ compound->type = type_build_compound_type(context.ast_alloc, compound);
+
+ return TYPE_MATCH_SUCCESS;
+ }
+
+ else if (node->kind == Ast_Kind_If_Expression) {
+ AstIfExpression* if_expr = (AstIfExpression *) node;
+
+ TypeMatch true_success = unify_node_and_type_(&if_expr->true_expr, type, permanent);
+ TypeMatch false_success = unify_node_and_type_(&if_expr->false_expr, type, permanent);
+
+ if (true_success == TYPE_MATCH_SUCCESS && false_success == TYPE_MATCH_SUCCESS) {
+ if (permanent) if_expr->type = type;
+ return TYPE_MATCH_SUCCESS;
+
+ } else if (true_success == TYPE_MATCH_FAILED || false_success == TYPE_MATCH_FAILED) {
+ return TYPE_MATCH_FAILED;
+
+ } else {
+ return TYPE_MATCH_YIELD;
+ }
+ }
+
+ else if (node->kind == Ast_Kind_Alias) {
+ AstAlias* alias = (AstAlias *) node;
+ return unify_node_and_type_(&alias->alias, type, permanent);
+ }
+
+ else if (node->kind == Ast_Kind_Address_Of) {
+ AstAddressOf *address_of = (AstAddressOf *) node;
+ if (address_of->can_be_removed) {
+ if (!permanent) {
+ return unify_node_and_type_(&address_of->expr, type, permanent);
+
+ } else {
+ *pnode = (AstTyped *) address_of->expr;
+ return unify_node_and_type_(pnode, type, permanent);
+ }
+ }
+ }
+
+ else if (node->kind == Ast_Kind_Zero_Value) {
+ if (node_type == NULL) {
+ node->type = type;
+ return TYPE_MATCH_SUCCESS; // Shouldn't this be on the next line? And have node_type == node->type checked?
+ }
+ }
+
+ return TYPE_MATCH_FAILED;
+}
+
+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);
+ }
+
+ node->type = type_build_compound_type(context.ast_alloc, (AstCompound *) node);
+ return node->type;
+ }
+
+ 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);
+ unify_node_and_type(&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->kind == Ast_Kind_Array_Literal && node->type == NULL) {
+ AstArrayLiteral* al = (AstArrayLiteral *) node;
+ Type* elem_type = &basic_types[Basic_Kind_Void];
+ if (bh_arr_length(al->values) > 0) {
+ elem_type = resolve_expression_type(al->values[0]);
+ }
+
+ if (elem_type) {
+ node->type = type_make_array(context.ast_alloc, elem_type, bh_arr_length(al->values));
+ node->flags |= Ast_Flag_Array_Literal_Typed;
+
+ if (node->entity == NULL) {
+ add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
+ }
+ }
+ }
+
+ if (node->kind == Ast_Kind_Struct_Literal && node->type == NULL) {
+ AstStructLiteral* sl = (AstStructLiteral *) node;
+ if (sl->stnode || sl->type_node) return NULL;
+
+ // If values without names are given to a struct literal without
+ // a type, then we cannot implicitly build the type of the struct
+ // literal, as the name of every member cannot be known. Maybe we
+ // could implicitly do something like _1, _2, ... for the members
+ // that we not given names?
+ if (bh_arr_length(sl->args.values) > 0) {
+ return NULL;
+ }
+
+ sl->type = type_build_implicit_type_of_struct_literal(context.ast_alloc, sl);
+ if (sl->type) {
+ add_entities_for_node(NULL, (AstNode *) sl, NULL, NULL);
+ }
+ }
+
+ // If polymorphic procedures HAVE to have a type, most likely
+ // because they are part of a `typeof` expression, they are
+ // assigned a void type. This is cleared before the procedure
+ // is solidified.
+ if (node->kind == Ast_Kind_Polymorphic_Proc) {
+ node->type = &basic_types[Basic_Kind_Void];
+ }
+
+ if (node->kind == Ast_Kind_Macro) {
+ return resolve_expression_type((AstTyped *) ((AstMacro *) node)->body);
+ }
+
+ 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) {
+ b32 big = bh_abs(((AstNumLit *) node)->value.l) >= (1ull << 32);
+ b32 unsign = ((AstNumLit *) node)->was_hex_literal;
+
+ if ( big && !unsign) convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_I64]);
+ else if ( big && unsign) convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_U64]);
+ else if (!big && !unsign) convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_I32]);
+ else if (!big && unsign) convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_U32]);
+ }
+ 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, b32 *is_valid) {
+ if (!node) return 0;
+
+ resolve_expression_type(node);
+
+ if (is_valid) *is_valid = 1;
+
+ 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, is_valid);
+ }
+
+ 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, is_valid);
+ }
+
+ if (node->kind == Ast_Kind_Enum_Value) {
+ return get_expression_integer_value(((AstEnumValue *) node)->value, is_valid);
+ }
+
+ if (node_is_type((AstNode*) node)) {
+ Type* type = type_build_from_ast(context.ast_alloc, (AstType *) node);
+ if (type) return type->id;
+ }
+
+ if (is_valid) *is_valid = 0;
+ return 0;
+}
+
+char *get_expression_string_value(AstTyped* node, b32 *out_is_valid) {
+ resolve_expression_type(node);
+
+ if (out_is_valid) *out_is_valid = 1;
+
+ if (node->kind == Ast_Kind_StrLit) {
+ AstStrLit *str = (AstStrLit *) node;
+
+ // CLEANUP: Maybe this should allocate on the heap?
+ // I guess if in all cases the memory is allocated on the heap,
+ // then the caller can free the memory.
+ i8* strdata = bh_alloc_array(global_heap_allocator, i8, str->token->length + 1);
+ i32 length = string_process_escape_seqs(strdata, str->token->text, str->token->length);
+ strdata[length] = '\0';
+
+ return strdata;
+ }
+
+ if (node->kind == Ast_Kind_Alias) {
+ return get_expression_string_value(((AstAlias *) node)->alias, out_is_valid);
+ }
+
+ if (out_is_valid) *out_is_valid = 0;
+ return NULL;
+}
+
+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_->id == to_->id) return 1;
+
+ 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;
+ }
+
+ // CLEANUP: These error messages should be a lot better and actually
+ // provide the types of the things in question.
+ if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
+ if (!types_are_compatible(to->Slice.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_DynArray) {
+ //if (!types_are_compatible(to->Slice.elem, from->DynArray.elem)) {
+ if (type_size_of(to->Slice.elem) != type_size_of(from->DynArray.elem)) {
+ *err_msg = "Dynmaic array to slice cast is not valid here because the types are different sizes.";
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_VarArgs) {
+ if (!types_are_compatible(to->Slice.elem, from->VarArgs.elem)) {
+ *err_msg = "Variadic argument to slice cast is not valid here because the types are different.";
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ if (to->kind == Type_Kind_Distinct) {
+ if (!types_are_compatible(to->Distinct.base_type, from)) {
+ // :BadErrorMessage
+ *err_msg = "Cannot convert to a distinct type using the wrong base type.";
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ if (from->kind == Type_Kind_Distinct) {
+ if (!types_are_compatible(from->Distinct.base_type, to)) {
+ // :BadErrorMessage
+ *err_msg = "Cannot convert from a distinct type to the wrong destination type.";
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ if (from->kind == Type_Kind_Slice || to->kind == Type_Kind_Slice) {
+ if ((from->kind != Type_Kind_Slice || to->kind != Type_Kind_Slice)
+ || to->Slice.elem->kind != Type_Kind_Pointer || from->Slice.elem->kind != Type_Kind_Pointer
+ || !types_are_compatible(from->Slice.elem, to->Slice.elem)) {
+ *err_msg = "Cannot only cast between slice types when both are a slice of compatible pointers.";
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ if (from->kind == Type_Kind_DynArray || to->kind == Type_Kind_DynArray) {
+ *err_msg = "Cannot cast to or from a dynamic array.";
+ return 0;
+ }
+
+ if (to->kind == Type_Kind_Function) {
+ *err_msg = "Cannot cast to a function.";
+ return 0;
+ }
+
+ if ( (type_is_simd(to) && !type_is_simd(from))
+ || (!type_is_simd(to) && type_is_simd(from))) {
+ *err_msg = "Can only perform a SIMD cast between SIMD types.";
+ return 0;
+ }
+
+ if (from->kind == Type_Kind_Basic && from->Basic.kind == Basic_Kind_Void) {
+ *err_msg = "Cannot cast from void.";
+ return 0;
+ }
+ i32 fromidx = -1, toidx = -1;
+ if (from->Basic.flags & Basic_Flag_Pointer || from->kind == Type_Kind_Array) {
+ fromidx = 10;
+ }
+ else if (from->Basic.flags & Basic_Flag_Integer) {
+ b32 unsign = (from->Basic.flags & Basic_Flag_Unsigned) != 0;
+
+ fromidx = log2_dumb(from->Basic.size) * 2 + unsign;
+ }
+ else if (from->Basic.flags & Basic_Flag_Float) {
+ if (from->Basic.size == 4) fromidx = 8;
+ else if (from->Basic.size == 8) fromidx = 9;
+ }
+ else if (from->Basic.flags & Basic_Flag_Boolean) {
+ fromidx = 0;
+ }
+ else if (from->Basic.flags & Basic_Flag_Type_Index) {
+ fromidx = 11;
+ }
+
+ if (to->Basic.flags & Basic_Flag_Pointer || to->kind == Type_Kind_Array) {
+ toidx = 10;
+ }
+ else if (to->Basic.flags & Basic_Flag_Integer) {
+ b32 unsign = (to->Basic.flags & Basic_Flag_Unsigned) != 0;
+
+ toidx = log2_dumb(to->Basic.size) * 2 + unsign;
+ }
+ else if (to->Basic.flags & Basic_Flag_Float) {
+ if (to->Basic.size == 4) toidx = 8;
+ else if (to->Basic.size == 8) toidx = 9;
+ }
+ else if (to->Basic.flags & Basic_Flag_Boolean) {
+ toidx = 0;
+ }
+ else if (to->Basic.flags & Basic_Flag_Type_Index) {
+ toidx = 11;
+ }
+
+ if (fromidx != -1 && toidx != -1) {
+ if (!cast_legality[fromidx][toidx]) {
+ *err_msg = bh_aprintf(global_heap_allocator, "Cast from '%s' to '%s' is not allowed.", type_get_name(from_), type_get_name(to_));
+ return 0;
+ }
+ } else {
+ *err_msg = bh_aprintf(global_heap_allocator, "Cast from '%s' to '%s' is not allowed.", type_get_name(from_), type_get_name(to_));
+ return 0;
+ }
+
+ *err_msg = NULL;
+ return 1;
+}
+
+char* get_function_name(AstFunction* func) {
+ if (func->kind != Ast_Kind_Function) return "<procedure>";
+
+ if (func->name != NULL) return func->name;
+
+ if (func->exported_name != NULL) {
+ return bh_aprintf(global_scratch_allocator,
+ "EXPORTED:%b",
+ func->exported_name->text,
+ func->exported_name->length);
+ }
+
+ return "<anonymous procedure>";
+}
+
+AstNode* strip_aliases(AstNode* n) {
+ if (n == NULL) return n;
+
+ while (n->kind == Ast_Kind_Alias) n = (AstNode *) ((AstAlias *) n)->alias;
+
+ return n;
+}
+
+AstNumLit* make_bool_literal(bh_allocator a, b32 b) {
+ AstNumLit* bl = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
+ bl->flags |= Ast_Flag_Comptime;
+ bl->type_node = (AstType *) &basic_type_bool;
+
+ bl->value.i = b ? 1 : 0;
+ return bl;
+}
+
+AstNumLit* make_int_literal(bh_allocator a, i64 i) {
+ AstNumLit* num = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
+ num->flags |= Ast_Flag_Comptime;
+
+ if (bh_abs(i) >= ((u64) 1 << 32))
+ num->type_node = (AstType *) &basic_type_i64;
+ else
+ num->type_node = (AstType *) &basic_type_i32;
+
+ num->value.l = i;
+ return num;
+}
+
+AstNumLit* make_float_literal(bh_allocator a, f64 d) {
+ // NOTE: Use convert_numlit_to_type to make this a concrete float
+ AstNumLit* num = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
+ num->flags |= Ast_Flag_Comptime;
+ num->type_node = (AstType *) &basic_type_float_unsized;
+ num->value.d = d;
+ return num;
+}
+
+AstRangeLiteral* make_range_literal(bh_allocator a, AstTyped* low, AstTyped* high) {
+ AstRangeLiteral* rl = onyx_ast_node_new(a, sizeof(AstRangeLiteral), Ast_Kind_Range_Literal);
+ rl->type = builtin_range_type_type;
+ rl->low = low;
+ rl->high = high;
+ return rl;
+}
+
+AstBinaryOp* make_binary_op(bh_allocator a, BinaryOp operation, AstTyped* left, AstTyped* right) {
+ AstBinaryOp* binop_node = onyx_ast_node_new(a, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
+ binop_node->left = left;
+ binop_node->right = right;
+ binop_node->operation = operation;
+ return binop_node;
+}
+
+AstArgument* make_argument(bh_allocator a, AstTyped* value) {
+ AstArgument* arg = onyx_ast_node_new(a, sizeof(AstArgument), Ast_Kind_Argument);
+ if (value->token) arg->token = value->token;
+ arg->value = value;
+ arg->type = value->type;
+ arg->next = NULL;
+ arg->va_kind = VA_Kind_Not_VA;
+ return arg;
+}
+
+AstFieldAccess* make_field_access(bh_allocator a, AstTyped* node, char* field) {
+ AstFieldAccess* fa = onyx_ast_node_new(a, sizeof(AstFieldAccess), Ast_Kind_Field_Access);
+ if (node->token) fa->token = node->token;
+ fa->field = field;
+ fa->expr = node;
+
+ return fa;
+}
+
+AstAddressOf* make_address_of(bh_allocator a, AstTyped* node) {
+ AstAddressOf* ao = onyx_ast_node_new(a, sizeof(AstAddressOf), Ast_Kind_Address_Of);
+ if (node->token) ao->token = node->token;
+ ao->expr = node;
+
+ return ao;
+}
+
+AstLocal* make_local(bh_allocator a, OnyxToken* token, AstType* type_node) {
+ AstLocal* local = onyx_ast_node_new(a, sizeof(AstLocal), Ast_Kind_Local);
+ local->token = token;
+ local->type_node = type_node;
+
+ return local;
+}
+
+AstNode* make_symbol(bh_allocator a, OnyxToken* sym) {
+ AstNode* symbol = onyx_ast_node_new(a, sizeof(AstNode), Ast_Kind_Symbol);
+ symbol->token = sym;
+ return symbol;
+}
+
+AstUnaryOp* make_cast(bh_allocator a, AstTyped* expr, Type* to) {
+ AstUnaryOp* cast = onyx_ast_node_new(a, sizeof(AstUnaryOp), Ast_Kind_Unary_Op);
+ cast->token = expr->token;
+ cast->operation = Unary_Op_Cast;
+ cast->expr = expr;
+ cast->type = to;
+ return cast;
+}
+
+AstZeroValue* make_zero_value(bh_allocator a, OnyxToken* token, Type* type) {
+ AstZeroValue* zero_value = onyx_ast_node_new(a, sizeof(AstZeroValue), Ast_Kind_Zero_Value);
+ zero_value->token = token;
+ zero_value->flags |= Ast_Flag_Comptime;
+ zero_value->type = type;
+ return zero_value;
+}
+
+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;
+ }
+
+ args->used_argument_count = -1;
+}
+
+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->used_argument_count = -1;
+ dest->named_values = src->named_values;
+
+ bh_arr_grow(dest->values, (u32) bh_arr_length(src->values));
+ bh_arr_set_length(dest->values, (u32) 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->used_argument_count = -1;
+ 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->used_argument_count = -1;
+ 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--;
+ }
+}
+
+void arguments_clear_baked_flags(Arguments* args) {
+ bh_arr_each(AstTyped *, arg, args->values) {
+ if ((*arg)->kind == Ast_Kind_Argument) {
+ ((AstArgument *) *arg)->is_baked = 0;
+ }
+ }
+
+ bh_arr_each(AstNamedValue *, arg, args->named_values) {
+ if ((*arg)->value->kind == Ast_Kind_Argument) {
+ ((AstArgument *) (*arg)->value)->is_baked = 0;
+ }
+ }
+}
+
+// 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, NULL);
+
+ return value != 0;
+}
+
+AstPolyCallType* convert_call_to_polycall(AstCall* call) {
+ // HACK HACK HACK
+ AstPolyCallType *pct = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyCallType), Ast_Kind_Poly_Call_Type);
+ pct->token = call->token;
+ pct->__unused = call->next;
+ pct->callee = (AstType *) call->callee;
+ pct->params = (AstNode **) call->args.values;
+ bh_arr_each(AstNode *, pp, pct->params) {
+ if ((*pp)->kind == Ast_Kind_Argument) {
+ *pp = (AstNode *) (*(AstArgument **) pp)->value;
+ }
+ }
+
+ return pct;
+}
--- /dev/null
+#include "astnodes.h"
+#include "types.h"
+#include "errors.h"
+#include "utils.h"
+
+AstBasicType basic_type_void = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Void] };
+AstBasicType basic_type_bool = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Bool] };
+AstBasicType basic_type_i8 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I8] };
+AstBasicType basic_type_u8 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_U8] };
+AstBasicType basic_type_i16 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I16] };
+AstBasicType basic_type_u16 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_U16] };
+AstBasicType basic_type_i32 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I32] };
+AstBasicType basic_type_u32 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_U32] };
+AstBasicType basic_type_i64 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I64] };
+AstBasicType basic_type_u64 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_U64] };
+AstBasicType basic_type_f32 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_F32] };
+AstBasicType basic_type_f64 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_F64] };
+AstBasicType basic_type_rawptr = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Rawptr] };
+AstBasicType basic_type_type_expr = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, 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, NULL, 0, NULL, &basic_types[Basic_Kind_Int_Unsized] };
+AstBasicType basic_type_float_unsized = { Ast_Kind_Basic_Type, 0, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Float_Unsized] };
+
+static OnyxToken simd_token = { Token_Type_Symbol, 0, "", { 0 } };
+AstBasicType basic_type_i8x16 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I8X16] };
+AstBasicType basic_type_i16x8 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I16X8] };
+AstBasicType basic_type_i32x4 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I32X4] };
+AstBasicType basic_type_i64x2 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I64X2] };
+AstBasicType basic_type_f32x4 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_F32X4] };
+AstBasicType basic_type_f64x2 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_F64X2] };
+AstBasicType basic_type_v128 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, 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, NULL, 0, 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 } };
+static OnyxToken builtin_tls_base_token = { Token_Type_Symbol, 10, "__tls_base ", { 0 } };
+static OnyxToken builtin_tls_size_token = { Token_Type_Symbol, 10, "__tls_size ", { 0 } };
+AstGlobal builtin_heap_start = { Ast_Kind_Global, Ast_Flag_Const, &builtin_heap_start_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
+AstGlobal builtin_stack_top = { Ast_Kind_Global, 0, &builtin_stack_top_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
+AstGlobal builtin_tls_base = { Ast_Kind_Global, 0, &builtin_tls_base_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
+AstGlobal builtin_tls_size = { Ast_Kind_Global, 0, &builtin_tls_size_token, NULL, NULL, (AstType *) &basic_type_u32, NULL };
+
+AstType *builtin_string_type;
+AstType *builtin_cstring_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;
+AstType *builtin_link_options_type;
+
+AstTyped *type_table_node = NULL;
+AstTyped *foreign_blocks_node = NULL;
+AstType *foreign_block_type = NULL;
+AstTyped *tagged_procedures_node = NULL;
+AstFunction *builtin_initialize_data_segments = NULL;
+AstFunction *builtin_run_init_procedures = NULL;
+bh_arr(AstFunction *) init_procedures = 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 },
+ { "builtin", "__tls_base", (AstNode *) &builtin_tls_base },
+ { "builtin", "__tls_size", (AstNode *) &builtin_tls_size },
+
+ { NULL, NULL, NULL },
+};
+
+IntrinsicTable 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 },
+
+ { "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 },
+
+ { "__atomic_wait", ONYX_INTRINSIC_ATOMIC_WAIT },
+ { "__atomic_notify", ONYX_INTRINSIC_ATOMIC_NOTIFY },
+
+ { "__atomic_fence", ONYX_INTRINSIC_ATOMIC_FENCE },
+
+ { "__atomic_load", ONYX_INTRINSIC_ATOMIC_LOAD },
+ { "__atomic_store", ONYX_INTRINSIC_ATOMIC_STORE },
+ { "__atomic_add", ONYX_INTRINSIC_ATOMIC_ADD },
+ { "__atomic_sub", ONYX_INTRINSIC_ATOMIC_SUB },
+ { "__atomic_and", ONYX_INTRINSIC_ATOMIC_AND },
+ { "__atomic_or", ONYX_INTRINSIC_ATOMIC_OR },
+ { "__atomic_xor", ONYX_INTRINSIC_ATOMIC_XOR },
+ { "__atomic_xchg", ONYX_INTRINSIC_ATOMIC_XCHG },
+ { "__atomic_cmpxchg", ONYX_INTRINSIC_ATOMIC_CMPXCHG },
+
+ { 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, context.global_scope->created_at);
+ assert(p);
+
+ symbol_builtin_introduce(p->scope, bsym->sym, bsym->node);
+ }
+ bsym++;
+ }
+
+ Package* p = package_lookup_or_create("builtin", context.global_scope, a, context.global_scope->created_at);
+
+ builtin_string_type = (AstType *) symbol_raw_resolve(p->scope, "str");
+ if (builtin_string_type == NULL) {
+ onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'str' struct not found in builtin package.");
+ return;
+ }
+
+ builtin_cstring_type = (AstType *) symbol_raw_resolve(p->scope, "cstr");
+ if (builtin_cstring_type == NULL) {
+ onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'cstr' type 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 }, Error_Critical, "'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 }, Error_Critical, "'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 }, Error_Critical, "'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 }, Error_Critical, "'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 }, Error_Critical, "'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 }, Error_Critical, "'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 }, Error_Critical, "'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 }, Error_Critical, "'Code' struct not found in builtin package.");
+ return;
+ }
+
+ builtin_initialize_data_segments = (AstFunction *) symbol_raw_resolve(p->scope, "__initialize_data_segments");
+ if (builtin_initialize_data_segments == NULL || builtin_initialize_data_segments->kind != Ast_Kind_Function) {
+ onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'__initialize_data_segments' procedure not found in builtin package.");
+ return;
+ }
+
+ builtin_run_init_procedures = (AstFunction *) symbol_raw_resolve(p->scope, "__run_init_procedures");
+ if (builtin_run_init_procedures == NULL || builtin_run_init_procedures->kind != Ast_Kind_Function) {
+ onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'__run_init_procedures' procedure not found.");
+ return;
+ }
+
+ builtin_link_options_type = (AstType *) symbol_raw_resolve(p->scope, "Link_Options");
+ if (builtin_link_options_type == NULL) {
+ onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'Link_Options' type not found.");
+ return;
+ }
+
+ bh_arr_new(global_heap_allocator, init_procedures, 4);
+
+ fori (i, 0, Binary_Op_Count) {
+ bh_arr_new(global_heap_allocator, operator_overloads[i], 4);
+ }
+
+ IntrinsicMap* intrinsic = &builtin_intrinsics[0];
+ while (intrinsic->name != NULL) {
+ shput(intrinsic_table, intrinsic->name, intrinsic->intrinsic);
+ intrinsic++;
+ }
+}
+
+void initalize_special_globals() {
+ Package *p = package_lookup("runtime.info");
+ if (p != NULL) {
+ type_table_node = (AstTyped *) symbol_raw_resolve(p->scope, "type_table");
+ foreign_blocks_node = (AstTyped *) symbol_raw_resolve(p->scope, "foreign_blocks");
+ foreign_block_type = (AstType *) symbol_raw_resolve(p->scope, "foreign_block");
+ tagged_procedures_node = (AstTyped *) symbol_raw_resolve(p->scope, "tagged_procedures");
+ }
+}
+
+void introduce_build_options(bh_allocator a) {
+ Package* p = package_lookup_or_create("runtime", context.global_scope, a, context.global_scope->created_at);
+
+ AstType* Runtime_Type = (AstType *) symbol_raw_resolve(p->scope, "Runtime");
+ if (Runtime_Type == NULL) {
+ onyx_report_error((OnyxFilePos) {0}, Error_Critical, "'Runtime' type not found in package runtime.");
+ return;
+ }
+
+ AstNumLit* runtime_type = make_int_literal(a, context.options->runtime);
+ runtime_type->type_node = Runtime_Type;
+ add_entities_for_node(NULL, (AstNode *) runtime_type, NULL, NULL);
+ symbol_builtin_introduce(p->scope, "runtime", (AstNode *) runtime_type);
+
+ AstNumLit* multi_threaded = make_int_literal(a, context.options->use_multi_threading);
+ multi_threaded->type_node = (AstType *) &basic_type_bool;
+ symbol_builtin_introduce(p->scope, "Multi_Threading_Enabled", (AstNode *) multi_threaded);
+
+ AstNumLit* wait_notify_available = make_int_literal(a, context.options->use_multi_threading && context.options->runtime == Runtime_Js);
+ wait_notify_available->type_node = (AstType *) &basic_type_bool;
+ symbol_builtin_introduce(p->scope, "Wait_Notify_Available", (AstNode *) wait_notify_available);
+
+ i32 os;
+ #ifdef _BH_LINUX
+ os = 1;
+ #endif
+ #ifdef _BH_WINDOWS
+ os = 2;
+ #endif
+
+ AstType* OS_Type = (AstType *) symbol_raw_resolve(p->scope, "OS");
+ if (OS_Type == NULL) {
+ onyx_report_error((OnyxFilePos) {0}, Error_Critical, "'OS' type not found in package runtime.");
+ return;
+ }
+
+ AstNumLit* os_type = make_int_literal(a, os);
+ os_type->type_node = OS_Type;
+ add_entities_for_node(NULL, (AstNode *) os_type, NULL, NULL);
+ symbol_builtin_introduce(p->scope, "compiler_os", (AstNode *) os_type);
+
+ i32 arch = 0;
+ #if defined(__x86_64__) || defined(_M_X64)
+ arch = 1; // X86_64;
+ #elif defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86)
+ arch = 2; // X86_32;
+ #elif defined(__aarch64__) || defined(_M_ARM64)
+ arch = 3; // AARCH64;
+ #endif
+
+ AstType* Arch_Type = (AstType *) symbol_raw_resolve(p->scope, "Arch");
+ if (Arch_Type == NULL) {
+ onyx_report_error((OnyxFilePos) {0}, Error_Critical, "'Arch' type not found in package runtime.");
+ return;
+ }
+
+ AstNumLit* arch_type = make_int_literal(a, arch);
+ arch_type->type_node = Arch_Type;
+ add_entities_for_node(NULL, (AstNode *) arch_type, NULL, NULL);
+ symbol_builtin_introduce(p->scope, "arch", (AstNode *) arch_type);
+
+ if (context.options->generate_foreign_info) {
+ AstNumLit* foreign_info = make_int_literal(a, 1);
+ foreign_info->type_node = (AstType *) &basic_type_bool;
+ symbol_builtin_introduce(p->scope, "Generated_Foreign_Info", (AstNode *) foreign_info);
+ }
+}
+
--- /dev/null
+#define BH_DEBUG
+#include "parser.h"
+#include "utils.h"
+
+// All of the `check` functions return a boolean that signals if an issue
+// was reached while processing the node. These error booleans propagate
+// up the call stack until they reach `check_entity`.
+
+#define CHECK(kind, ...) do { \
+ CheckStatus cs = check_ ## kind (__VA_ARGS__); \
+ if (cs > Check_Errors_Start) return cs; \
+ } while (0)
+
+#define YIELD(loc, msg) do { \
+ if (context.cycle_detected) { \
+ onyx_report_error(loc, Error_Waiting_On, msg); \
+ return Check_Error; \
+ } else { \
+ return Check_Yield_Macro; \
+ } \
+ } while (0)
+
+#define YIELD_(loc, msg, ...) do { \
+ if (context.cycle_detected) { \
+ onyx_report_error(loc, Error_Waiting_On, msg, __VA_ARGS__); \
+ return Check_Error; \
+ } else { \
+ return Check_Yield_Macro; \
+ } \
+ } while (0)
+
+#define YIELD_ERROR(loc, msg) do { \
+ if (context.cycle_detected) { \
+ onyx_report_error(loc, Error_Critical, msg); \
+ return Check_Error; \
+ } else { \
+ return Check_Yield_Macro; \
+ } \
+ } while (0)
+
+#define ERROR(loc, msg) do { \
+ onyx_report_error(loc, Error_Critical, msg); \
+ return Check_Error; \
+ } while (0)
+
+#define ERROR_(loc, msg, ...) do { \
+ onyx_report_error(loc, Error_Critical, msg, __VA_ARGS__); \
+ return Check_Error; \
+ } while (0)
+
+#define TYPE_CHECK_(expr, type, type_name) \
+ TypeMatch type_name; \
+ type_name = unify_node_and_type(expr, type); \
+ if (type_name == TYPE_MATCH_YIELD) YIELD((*expr)->token->pos, "Waiting on type checking."); \
+ if (type_name == TYPE_MATCH_FAILED)
+
+#define CONCAT(a, b) a##_##b
+#define DEFER_LINE(a, line) CONCAT(a, line)
+#define TYPE_CHECK(expr, type) TYPE_CHECK_(expr, type, DEFER_LINE(tc, __LINE__))
+
+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_Failed, // The node is done processing and should be put in the state of Failed.
+ 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** paof);
+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_temp_function_header(AstFunction* func);
+CheckStatus check_function_header(AstFunction* func);
+CheckStatus check_memres_type(AstMemRes* memres);
+CheckStatus check_memres(AstMemRes* memres);
+CheckStatus check_type(AstType** ptype);
+CheckStatus check_insert_directive(AstDirectiveInsert** pinsert);
+CheckStatus check_directive_solidify(AstDirectiveSolidify** psolid);
+CheckStatus check_do_block(AstDoBlock** pdoblock);
+CheckStatus check_constraint(AstConstraint *constraint);
+CheckStatus check_constraint_context(ConstraintContext *cc, Scope *scope, OnyxFilePos pos);
+CheckStatus check_polyquery(AstPolyQuery *query);
+
+// HACK HACK HACK
+b32 expression_types_must_be_known = 0;
+b32 all_checks_are_final = 1;
+b32 inside_for_iterator = 0;
+
+#define STATEMENT_LEVEL 1
+#define EXPRESSION_LEVEL 2
+u32 current_checking_level=0;
+
+static inline void fill_in_type(AstTyped* node) {
+ if (node->type == NULL) {
+ if (check_type(&node->type_node) > Check_Errors_Start) return;
+
+ 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_ERROR(retnode->token->pos, "Unable to determine the automatic return type here.");
+
+ *expected_return_type = retnode->expr->type;
+ return Check_Success;
+ }
+
+ TYPE_CHECK(&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));
+ }
+
+ //
+ // Catch the obvious case of return '^.{ ... }', as that will never
+ // be legal.
+ if (retnode->expr->kind == Ast_Kind_Address_Of) {
+ AstAddressOf *aof = (AstAddressOf *) retnode->expr;
+ if (node_is_addressable_literal((AstNode *) aof->expr)) {
+ ERROR(retnode->token->pos, "Cannot return a pointer to a literal, as the space reserved for the literal will be freed upon returning.");
+ }
+ }
+
+ } 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);
+ ifnode->true_stmt->rules = Block_Rule_Macro;
+ }
+
+ } else {
+ if (ifnode->false_stmt != NULL) {
+ CHECK(statement, (AstNode **) &ifnode->false_stmt);
+ ifnode->false_stmt->rules = Block_Rule_Macro;
+ }
+ }
+
+ } 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) {
+ if (whilenode->bottom_test) {
+ ERROR(whilenode->token->pos, "while-loops with an 'else' clause cannot be bottom tested.");
+ }
+
+ CHECK(statement, (AstNode **) &whilenode->false_stmt);
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_for(AstFor* fornode) {
+ b32 old_inside_for_iterator;
+ if (fornode->flags & Ast_Flag_Has_Been_Checked) goto fornode_expr_checked;
+
+ 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.");
+
+ OnyxFilePos error_loc = fornode->var->token->pos;
+ if (error_loc.filename == NULL) {
+ error_loc = fornode->token->pos;
+ }
+
+ fornode->loop_type = For_Loop_Invalid;
+ if (types_are_compatible(iter_type, &basic_types[Basic_Kind_I32])) {
+ if (fornode->by_pointer) {
+ ERROR(error_loc, "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(error_loc, "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 = type_make_pointer(context.ast_alloc, iter_type->Slice.elem);
+ else fornode->var->type = iter_type->Slice.elem;
+
+ fornode->loop_type = For_Loop_Slice;
+
+ }
+ else if (iter_type->kind == Type_Kind_VarArgs) {
+ if (fornode->by_pointer) {
+ ERROR_(error_loc, "Cannot iterate by pointer over '%s'.", type_get_name(iter_type));
+ }
+
+ fornode->var->type = iter_type->VarArgs.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 = type_make_pointer(context.ast_alloc, iter_type->DynArray.elem);
+ else fornode->var->type = iter_type->DynArray.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(error_loc, "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_(error_loc, "Cannot iterate over a '%s'.", type_get_name(iter_type));
+
+ if (fornode->no_close && fornode->loop_type != For_Loop_Iterator) {
+ onyx_report_warning(error_loc, "Warning: #no_close here is meaningless as the iterable is not an iterator.");
+ }
+
+ fornode->flags |= Ast_Flag_Has_Been_Checked;
+
+fornode_expr_checked:
+ old_inside_for_iterator = inside_for_iterator;
+ inside_for_iterator = 0;
+ iter_type = fornode->iter->type;
+ if (type_struct_constructed_from_poly_struct(iter_type, builtin_iterator_type)) {
+ inside_for_iterator = 1;
+ }
+
+ do {
+ CheckStatus cs = check_block(fornode->stmt);
+ inside_for_iterator = old_inside_for_iterator;
+ if (cs > Check_Errors_Start) return cs;
+ } while(0);
+
+ return Check_Success;
+}
+
+static b32 add_case_to_switch_statement(AstSwitch* switchnode, u64 case_value, AstBlock* block, OnyxFilePos pos) {
+ assert(switchnode->switch_kind == Switch_Kind_Integer);
+
+ 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, Error_Critical, "Multiple cases for values '%d'.", case_value);
+ return 1;
+ }
+
+ bh_imap_put(&switchnode->case_map, case_value, (u64) block);
+ return 0;
+}
+
+static CheckStatus collect_switch_case_blocks(AstSwitch* switchnode, AstBlock* root) {
+ AstNode *walker = root->body;
+ while (walker != NULL) {
+ switch (walker->kind) {
+ case Ast_Kind_Block:
+ collect_switch_case_blocks(switchnode, (AstBlock *) walker);
+ break;
+
+ case Ast_Kind_Switch_Case: {
+ AstSwitchCase *case_node = (AstSwitchCase *) walker;
+ if (case_node->is_default) {
+ if (switchnode->default_case != NULL && switchnode->default_case != case_node->block) {
+ ERROR(case_node->token->pos, "Multiple #default cases given");
+ ERROR(switchnode->default_case->token->pos, "Multiple #default cases given");
+ return Check_Error;
+ }
+
+ switchnode->default_case = case_node->block;
+ } else {
+ bh_arr_push(switchnode->cases, case_node);
+ }
+ break;
+ }
+
+ default:
+ ERROR(walker->token->pos, "This statement is not allowed here.");
+ }
+
+ walker = walker->next;
+ }
+
+ return Check_Success;
+}
+
+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 (!(switchnode->flags & Ast_Flag_Has_Been_Checked)) {
+ if (resolved_expr_type == NULL) YIELD(switchnode->token->pos, "Waiting for expression type to be known.");
+
+ switchnode->switch_kind = Switch_Kind_Integer;
+ if (!type_is_integer(switchnode->expr->type) && switchnode->expr->type->kind != Type_Kind_Enum) {
+ switchnode->switch_kind = Switch_Kind_Use_Equals;
+ }
+
+ switch (switchnode->switch_kind) {
+ case Switch_Kind_Integer:
+ switchnode->min_case = 0xffffffffffffffff;
+ bh_imap_init(&switchnode->case_map, global_heap_allocator, 4);
+ break;
+
+ case Switch_Kind_Use_Equals:
+ bh_arr_new(global_heap_allocator, switchnode->case_exprs, 4);
+ break;
+
+ default: assert(0);
+ }
+ }
+ switchnode->flags |= Ast_Flag_Has_Been_Checked;
+
+ // Should the case block code be checked here?
+ // Or should this just exist to resolve macros and expand #unquotes
+ // then the cases are consumed into the array or cases, THEN the blocks
+ // are actually checked?
+ if (switchnode->cases == NULL) {
+ CHECK(block, switchnode->case_block);
+
+ bh_arr_new(global_heap_allocator, switchnode->cases, 4);
+ if (collect_switch_case_blocks(switchnode, switchnode->case_block) != Check_Success) {
+ return Check_Error;
+ }
+
+ // This is important, otherwise if this block has to return to symbol resolution.
+ switchnode->case_block->statement_idx = 0;
+ }
+
+ fori (i, switchnode->yield_return_index, bh_arr_length(switchnode->cases)) {
+ AstSwitchCase *sc = switchnode->cases[i];
+ CHECK(block, sc->block);
+
+ bh_arr_each(AstTyped *, value, sc->values) {
+ CHECK(expression, value);
+
+ if (switchnode->switch_kind == Switch_Kind_Integer && (*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;
+ }
+
+ TYPE_CHECK(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));
+ }
+
+ switch (switchnode->switch_kind) {
+ case Switch_Kind_Integer: {
+ b32 is_valid;
+ i64 integer_value = get_expression_integer_value(*value, &is_valid);
+ if (!is_valid)
+ ERROR_((*value)->token->pos, "Case statement expected compile time known integer. Got '%s'.", onyx_ast_node_kind_string((*value)->kind));
+
+ if (add_case_to_switch_statement(switchnode, integer_value, sc->block, sc->block->token->pos))
+ return Check_Error;
+
+ break;
+ }
+
+ case Switch_Kind_Use_Equals: {
+ // Gross
+ b32 found = 0;
+ bh_arr_each(CaseToBlock, ctb, switchnode->case_exprs) {
+ if (ctb->original_value == *value) {
+ CHECK(expression, (AstTyped **) &ctb->comparison);
+ found = 1;
+ break;
+ }
+ }
+ if (found) break;
+
+ CaseToBlock ctb;
+ ctb.block = sc->block;
+ ctb.original_value = *value;
+ ctb.comparison = make_binary_op(context.ast_alloc, Binary_Op_Equal, switchnode->expr, *value);
+ ctb.comparison->token = (*value)->token;
+ bh_arr_push(switchnode->case_exprs, ctb);
+
+ CHECK(binaryop, &bh_arr_last(switchnode->case_exprs).comparison);
+ break;
+ }
+ }
+ }
+
+ switchnode->yield_return_index += 1;
+ }
+
+ 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 CheckStatus check_resolve_callee(AstCall* call, AstTyped** effective_callee) {
+ if (call->kind == Ast_Kind_Intrinsic_Call) return Check_Success;
+
+ AstTyped* callee = (AstTyped *) strip_aliases((AstNode *) call->callee);
+ b32 calling_a_macro = 0;
+
+ if (callee->kind == Ast_Kind_Overloaded_Function) {
+ AstTyped* new_callee = find_matching_overload_by_arguments(
+ ((AstOverloadedFunction *) callee)->overloads,
+ &call->args);
+
+ if (new_callee == NULL) {
+ report_unable_to_match_overload(call, ((AstOverloadedFunction *) callee)->overloads);
+ return Check_Error;
+ }
+
+ if (new_callee == (AstTyped *) &node_that_signals_a_yield) {
+ 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, 1);
+ 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 while (callee->kind == Ast_Kind_Polymorphic_Proc) {
+ AstTyped* new_callee = (AstTyped *) polymorphic_proc_lookup((AstFunction *) 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;
+ }
+
+ // 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 (!calling_a_macro) call->callee = 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;
+}
+
+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->kind == Ast_Kind_Call) {
+ AstNode* callee = strip_aliases((AstNode *) call->callee);
+ if (callee->kind == Ast_Kind_Poly_Struct_Type) {
+ *pcall = (AstCall *) convert_call_to_polycall(call);
+ CHECK(expression, (AstTyped **) pcall);
+ return Check_Success;
+ }
+ }
+
+ 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;
+
+ AstFunction* callee=NULL;
+ CHECK(resolve_callee, call, (AstTyped **) &callee);
+
+ if (callee->kind == Ast_Kind_Function) {
+ if (callee->constraints.constraints != NULL && callee->constraints.constraints_met == 0) {
+ YIELD(call->token->pos, "Waiting for constraints to be checked on callee.");
+ }
+ }
+
+ i32 arg_count = get_argument_buffer_size(&callee->type->Function, &call->args);
+ arguments_ensure_length(&call->args, arg_count);
+
+ char* err_msg = NULL;
+ fill_in_arguments(&call->args, (AstNode *) callee, &err_msg, 0);
+ 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->data_id = 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->kind == Ast_Kind_Function && callee->is_intrinsic) {
+ call->kind = Ast_Kind_Intrinsic_Call;
+ call->callee = NULL;
+
+ token_toggle_end(callee->intrinsic_name);
+ char* intr_name = callee->intrinsic_name->text;
+
+ i32 index;
+ if ((index = shgeti(intrinsic_table, intr_name)) == -1) {
+ onyx_report_error(callee->token->pos, Error_Critical, "Intrinsic not supported, '%s'.", intr_name);
+ token_toggle_end(callee->intrinsic_name);
+ return Check_Error;
+ }
+
+ call->intrinsic = intrinsic_table[index].value;
+
+ 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.");
+ }
+
+ OnyxError error;
+ TypeMatch tm = check_arguments_against_type(&call->args, &callee->type->Function, &call->va_kind,
+ call->token, get_function_name(callee), &error);
+ if (tm == TYPE_MATCH_FAILED) {
+ onyx_submit_error(error);
+ return Check_Error;
+ }
+
+ if (tm == TYPE_MATCH_YIELD) YIELD(call->token->pos, "Waiting on argument type checking.");
+
+ call->flags |= Ast_Flag_Has_Been_Checked;
+ 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, Error_Critical, "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, AstTyped* third_argument) {
+ if (bh_arr_length(operator_overloads[binop->operation]) == 0) return NULL;
+
+ if (binop->overload_args == NULL) {
+ binop->overload_args = bh_alloc_item(context.ast_alloc, Arguments);
+ bh_arr_new(context.ast_alloc, binop->overload_args->values, 3);
+ bh_arr_set_length(binop->overload_args->values, third_argument ? 3 : 2);
+
+ if (binop_is_assignment(binop->operation)) {
+ binop->overload_args->values[0] = (AstTyped *) make_address_of(context.ast_alloc, binop->left);
+
+ u32 current_all_checks_are_final = all_checks_are_final;
+ all_checks_are_final = 0;
+ u32 current_checking_level_store = current_checking_level;
+ CheckStatus cs = check_address_of((AstAddressOf **) &binop->overload_args->values[0]);
+ current_checking_level = current_checking_level_store;
+ all_checks_are_final = current_all_checks_are_final;
+
+ if (cs == Check_Yield_Macro) return (AstCall *) &node_that_signals_a_yield;
+ if (cs == Check_Error) return NULL;
+
+ binop->overload_args->values[0] = (AstTyped *) make_argument(context.ast_alloc, binop->overload_args->values[0]);
+
+ } else {
+ binop->overload_args->values[0] = (AstTyped *) make_argument(context.ast_alloc, binop->left);
+ }
+
+
+ binop->overload_args->values[1] = (AstTyped *) make_argument(context.ast_alloc, binop->right);
+ if (third_argument != NULL) binop->overload_args->values[2] = (AstTyped *) make_argument(context.ast_alloc, third_argument);
+ }
+
+ AstTyped* overload = find_matching_overload_by_arguments(operator_overloads[binop->operation], binop->overload_args);
+ if (overload == NULL || overload == (AstTyped *) &node_that_signals_a_yield) return (AstCall *) overload;
+
+ 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;
+
+ arguments_clone(&implicit_call->args, binop->overload_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) {
+ if (binop->left->type_node != NULL && binop->left->entity && binop->left->entity->state <= Entity_State_Check_Types) {
+ YIELD(binop->token->pos, "Waiting for type to be constructed on left hand side.");
+ }
+
+ // NOTE: There is a subtlety here. You cannot use the result of `resolve_expression_type` directly,
+ // as in some cases (especially with macros and polyprocs), the result is not "correct". The result
+ // makes them appears as though they are runtime-known values, which they are not. Using the following
+ // pattern does prevent this issue.
+ resolve_expression_type(binop->right);
+
+ Type* right_type = get_expression_type(binop->right);
+ if (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 (right_type->kind == Type_Kind_Compound) {
+ AstCompound* lhs = (AstCompound *) binop->left;
+ i32 expr_count = right_type->Compound.count;
+ if (lhs->kind != Ast_Kind_Compound || bh_arr_length(lhs->exprs) != expr_count) {
+ ERROR_(binop->token->pos, "Expected left hand side to have %d expressions.", expr_count);
+ }
+
+ fori (i, 0, expr_count) lhs->exprs[i]->type = right_type->Compound.types[i];
+
+ lhs->type = type_build_compound_type(context.ast_alloc, lhs);
+
+ } else {
+ binop->left->type = right_type;
+ }
+ }
+
+ } else {
+ // NOTE: +=, -=, ...
+ // NOTE: At this point, it is assumed that operator overloads for +=, -=, etc have been tested.
+
+ 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.");
+ }
+ }
+
+ TYPE_CHECK(&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 == NULL) YIELD(binop->token->pos, "Waiting for left-type to be known.");
+ if (rtype == NULL) YIELD(binop->token->pos, "Waiting for right-type to be known.");
+
+ 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.");
+
+ TYPE_CHECK(&binop->left, rtype) {
+ TYPE_CHECK(&binop->right, ltype) {
+ ERROR_(binop->token->pos,
+ "Cannot compare '%s' to '%s'.",
+ type_get_name(binop->left->type),
+ type_get_name(binop->right->type));
+ }
+ }
+ }
+
+ 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;
+
+ if (binop->operation == Binary_Op_Assign && binop->left->kind == Ast_Kind_Subscript && bh_arr_length(operator_overloads[Binary_Op_Subscript_Equals]) > 0) {
+ AstSubscript* sub = (AstSubscript *) binop->left;
+
+ if (binop->potential_substitute == NULL) {
+ u32 current_checking_level_store = current_checking_level;
+ CHECK(expression, &sub->addr);
+ CHECK(expression, &sub->expr);
+ CHECK(expression, &binop->right);
+ current_checking_level = current_checking_level_store;
+
+ AstBinaryOp *op = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
+ op->token = binop->token;
+ op->operation = Binary_Op_Subscript_Equals;
+ op->left = ((AstSubscript *) binop->left)->addr;
+ op->right = ((AstSubscript *) binop->left)->expr;
+
+ binop->potential_substitute = op;
+ }
+
+ AstCall* call = binaryop_try_operator_overload(binop->potential_substitute, binop->right);
+ if (call == (AstCall *) &node_that_signals_a_yield) YIELD(binop->token->pos, "Waiting on potential operator overload.");
+ if (call != NULL) {
+ call->next = binop->next;
+ *(AstCall **) pbinop = call;
+
+ CHECK(call, (AstCall **) pbinop);
+ 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;
+
+ // :UnaryFieldAccessIsGross
+ if (binop->left->kind == Ast_Kind_Unary_Field_Access || binop->right->kind == Ast_Kind_Unary_Field_Access) {
+ TYPE_CHECK(&binop->left, binop->right->type) {
+ TYPE_CHECK(&binop->right, binop->left->type) {
+ // TODO: This should report a better error about the Unary_Field_Access not be able to be resolved given whatever type.
+ // - brendanfh 2021/12/31
+ report_bad_binaryop(binop);
+ return Check_Error;
+ }
+ }
+ }
+
+ 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.");
+ }
+ }
+
+ // NOTE: Try operator overloading before checking everything else.
+ if ((binop->left->type != NULL && (binop->left->type->kind != Type_Kind_Basic || (binop->left->type->Basic.flags & Basic_Flag_SIMD) != 0))
+ || (binop->right->type != NULL && (binop->right->type->kind != Type_Kind_Basic || (binop->right->type->Basic.flags & Basic_Flag_SIMD) != 0))) {
+ AstCall *implicit_call = binaryop_try_operator_overload(binop, NULL);
+
+ 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);
+
+ if (binop->left->type == NULL && binop->left->entity && binop->left->entity->state <= Entity_State_Check_Types) {
+ YIELD(binop->left->token->pos, "Waiting for this type to be known");
+ }
+ if (binop->right->type == NULL && binop->right->entity && binop->right->entity->state <= Entity_State_Check_Types) {
+ YIELD(binop->right->token->pos, "Waiting for this type to be known");
+ }
+
+ // 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.");
+ }
+
+ TYPE_CHECK(&binop->left, binop->right->type) {
+ TYPE_CHECK(&binop->right, binop->left->type) {
+ 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];
+ }
+
+ if (all_checks_are_final) {
+ 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) {
+ CHECK(arguments, &sl->args);
+
+ return Check_Success;
+ }
+
+ CHECK(expression, &sl->stnode);
+ if (!node_is_type((AstNode *) sl->stnode)) {
+ ERROR(sl->token->pos, "Type used for struct literal is not a type.");
+ }
+
+ sl->type = type_build_from_ast(context.ast_alloc, (AstType *) sl->stnode);
+ if (sl->type == NULL)
+ YIELD(sl->token->pos, "Trying to resolve type of struct literal.");
+ }
+
+ if (!type_is_structlike_strict(sl->type)) {
+ //
+ // If there are no given arguments to a structure literal, it is treated as a 'zero-value',
+ // and can be used to create a completely zeroed value of any type.
+ if (bh_arr_length(sl->args.values) == 0 && bh_arr_length(sl->args.named_values) == 0) {
+ AstZeroValue *zv = make_zero_value(context.ast_alloc, sl->token, sl->type);
+ bh_arr_push(sl->args.values, (AstTyped *) zv);
+
+ sl->flags |= Ast_Flag_Has_Been_Checked;
+ return Check_Success;
+ }
+
+ if ((sl->flags & Ast_Flag_Has_Been_Checked) != 0) {
+ assert(sl->args.values);
+ assert(sl->args.values[0]);
+ assert(sl->args.values[0]->kind == Ast_Kind_Zero_Value);
+ return Check_Success;
+ }
+
+ //
+ // Otherwise, it is not possible to construct the type if it is not a structure.
+ 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, 1)) {
+ onyx_report_error(sl->token->pos, Error_Critical, 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, Error_Critical,
+ "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);
+ 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.");
+ }
+
+ TYPE_CHECK(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) return Check_Success;
+ // YIELD(al->token->pos, "Waiting for array literal type to be known.");
+
+ CHECK(expression, &al->atnode);
+ if (!node_is_type((AstNode *) al->atnode))
+ ERROR(al->token->pos, "Array type is not a type.");
+
+ al->type = type_build_from_ast(context.ast_alloc, (AstType *) al->atnode);
+ 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;
+ assert(al->type->kind == Type_Kind_Array);
+
+ 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);
+
+ TYPE_CHECK(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;
+ if (range->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
+ CHECK(expression, &range->low);
+ CHECK(expression, &range->high);
+
+ builtin_range_type_type = type_build_from_ast(context.ast_alloc, builtin_range_type);
+ if (builtin_range_type_type == NULL) YIELD(range->token->pos, "Waiting for 'range' structure to be built.");
+
+ Type* expected_range_type = builtin_range_type_type;
+ StructMember smem;
+
+ type_lookup_member(expected_range_type, "low", &smem);
+ TYPE_CHECK(&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);
+ TYPE_CHECK(&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;
+ }
+
+ range->flags |= Ast_Flag_Has_Been_Checked;
+ 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);
+
+ TYPE_CHECK(&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) 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** paof) {
+ AstAddressOf* aof = *paof;
+
+ AstTyped* expr = (AstTyped *) strip_aliases((AstNode *) aof->expr);
+ if (expr->kind == Ast_Kind_Subscript && bh_arr_length(operator_overloads[Binary_Op_Ptr_Subscript]) > 0) {
+ if (aof->potential_substitute == NULL) {
+ CHECK(expression, &((AstSubscript *) expr)->addr);
+ CHECK(expression, &((AstSubscript *) expr)->expr);
+
+ AstBinaryOp *op = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
+ op->operation = Binary_Op_Ptr_Subscript;
+ op->left = ((AstSubscript *) expr)->addr;
+ op->right = ((AstSubscript *) expr)->expr;
+ op->token = aof->token;
+
+ aof->potential_substitute = op;
+ }
+
+ AstCall* call = binaryop_try_operator_overload(aof->potential_substitute, NULL);
+ if (call == (AstCall *) &node_that_signals_a_yield) YIELD(aof->token->pos, "Waiting for operator overload to possibly resolve.");
+ if (call != NULL) {
+ call->next = aof->next;
+ *(AstCall **) paof = call;
+
+ CHECK(call, (AstCall **) paof);
+ return Check_Success;
+ }
+ }
+
+ CHECK(expression, &aof->expr);
+ if (node_is_addressable_literal((AstNode *) aof->expr)) {
+ resolve_expression_type(aof->expr);
+ }
+
+ if (aof->expr->type == NULL) {
+ YIELD(aof->token->pos, "Trying to resolve type of expression to take a reference.");
+ }
+
+ expr = (AstTyped *) strip_aliases((AstNode *) aof->expr);
+ if (node_is_type((AstNode *) expr)) {
+ AstPointerType *pt = onyx_ast_node_new(context.ast_alloc, sizeof(AstPointerType), Ast_Kind_Pointer_Type);
+ pt->token = aof->token;
+ pt->elem = (AstType *) expr;
+ pt->__unused = aof->next;
+ *paof = (AstAddressOf *) pt;
+ CHECK(type, (AstType **) &pt);
+ return Check_Success;
+ }
+
+ if ((expr->kind != Ast_Kind_Subscript
+ && expr->kind != Ast_Kind_Dereference
+ && expr->kind != Ast_Kind_Field_Access
+ && expr->kind != Ast_Kind_Memres
+ && expr->kind != Ast_Kind_Local
+ && expr->kind != Ast_Kind_Constraint_Sentinel
+ && !node_is_addressable_literal((AstNode *) expr))
+ || (expr->flags & Ast_Flag_Cannot_Take_Addr) != 0) {
+
+ if (aof->can_be_removed) {
+ *(AstTyped **) paof = aof->expr;
+ return Check_Yield_Macro;
+ }
+
+ ERROR_(aof->token->pos, "Cannot take the address of something that is not an l-value. %s", onyx_ast_node_kind_string(expr->kind));
+ }
+
+ expr->flags |= Ast_Flag_Address_Taken;
+
+ aof->type = type_make_pointer(context.ast_alloc, 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);
+
+ if (sub->addr->type == NULL) YIELD(sub->token->pos, "Waiting to know type of left-hand side of subscript.");
+
+ // NOTE: Try operator overloading before checking everything else.
+ if (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, NULL);
+
+ 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 (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;
+ }
+
+ if (types_are_compatible(sub->expr->type, builtin_range_type_type)) {
+ Type *of = type_get_contained_type(sub->addr->type);
+ if (of == NULL) {
+ // 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 (!type_is_small_integer(sub->expr->type)) {
+ report_bad_binaryop((AstBinaryOp *) sub);
+ ERROR_(sub->token->pos, "Expected small integer type for index, got '%s'.", node_get_type_name(sub->expr));
+ }
+
+ sub->type = type_get_contained_type(sub->addr->type);
+ if (sub->type == NULL) {
+ 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;
+ if (field->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
+ CHECK(expression, &field->expr);
+ if (field->expr->type == NULL) {
+ 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;
+ }
+
+ if (field->token != NULL && field->field == NULL) {
+ token_toggle_end(field->token);
+ field->field = bh_strdup(context.ast_alloc, field->token->text);
+ token_toggle_end(field->token);
+ }
+
+ if (field->expr->type->kind == Type_Kind_Struct) {
+ if (field->expr->type->Struct.status != SPS_Uses_Done) {
+ YIELD(field->token->pos, "Waiting for struct type to be completed before looking up members.");
+ }
+ }
+
+ StructMember smem;
+ if (!type_lookup_member(field->expr->type, field->field, &smem)) {
+ if (field->expr->type->kind == Type_Kind_Array) {
+ if (!strcmp(field->field, "count")) {
+ *pfield = (AstFieldAccess *) make_int_literal(context.ast_alloc, field->expr->type->Array.count);
+ return Check_Success;
+ }
+ }
+
+ AstNode* n = try_symbol_raw_resolve_from_type(field->expr->type, field->field);
+
+ AstType* type_node = field->expr->type->ast_type;
+ if (!n) n = try_symbol_raw_resolve_from_node((AstNode *) type_node, field->field);
+
+ if (n) {
+ *pfield = (AstFieldAccess *) n;
+ return Check_Success;
+ }
+
+ char* closest = find_closest_symbol_in_node((AstNode *) type_node, field->field);
+ if (closest) {
+ ERROR_(field->token->pos, "Field '%s' does not exists on '%s'. Did you mean '%s'?", field->field, node_get_type_name(field->expr), closest);
+ } else {
+ ERROR_(field->token->pos, "Field '%s' does not exists on '%s'.", field->field, node_get_type_name(field->expr));
+ }
+ }
+
+ // NOTE: If this member was included into the structure through a "use x: ^T" kind of statement,
+ // then we have to insert a intermediate field access in order to access the correct member.
+ if (smem.use_through_pointer_index >= 0) {
+ StructMember containing_member;
+ assert(type_lookup_member_by_idx(field->expr->type, smem.use_through_pointer_index, &containing_member));
+
+ AstFieldAccess *new_access = onyx_ast_node_new(context.ast_alloc, sizeof(AstFieldAccess), Ast_Kind_Field_Access);
+ new_access->token = field->token;
+ new_access->offset = containing_member.offset;
+ new_access->idx = containing_member.idx;
+ new_access->type = containing_member.type;
+ new_access->expr = field->expr;
+ new_access->flags |= Ast_Flag_Has_Been_Checked;
+
+ field->expr = (AstTyped *) new_access;
+ }
+
+ field->offset = smem.offset;
+ field->idx = smem.idx;
+ field->type = smem.type;
+ field->flags |= Ast_Flag_Has_Been_Checked;
+ return Check_Success;
+}
+
+CheckStatus check_method_call(AstBinaryOp** pmcall) {
+ AstBinaryOp* mcall = *pmcall;
+ CHECK(expression, &mcall->left);
+ if (mcall->left->type == NULL) YIELD(mcall->token->pos, "Trying to resolve type of left hand side.");
+
+ mcall->type = mcall->left->type;
+ 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) {
+ AstAddressOf *address_of = make_address_of(context.ast_alloc, implicit_argument);
+ address_of->can_be_removed = 1;
+ implicit_argument = (AstTyped *) address_of;
+ }
+
+ 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;
+
+ *pmcall = (AstBinaryOp *) mcall->right;
+ mcall->right->next = mcall->next;
+ }
+ mcall->flags |= Ast_Flag_Has_Been_Checked;
+
+ CHECK(call, (AstCall **) pmcall);
+ return Check_Success;
+}
+
+CheckStatus check_size_of(AstSizeOf* so) {
+ 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);
+ so->flags |= Ast_Flag_Comptime;
+
+ return Check_Success;
+}
+
+CheckStatus check_align_of(AstAlignOf* ao) {
+ 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);
+ ao->flags |= Ast_Flag_Comptime;
+
+ 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.
+ CHECK(type, (AstType **) pexpr);
+ expr = *pexpr;
+
+ // 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.");
+ }
+ } else {
+ type_build_from_ast(context.ast_alloc, (AstType*) expr);
+ }
+
+ 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;
+ }
+
+ if (expr->kind == Ast_Kind_Directive_Init) {
+ ERROR(expr->token->pos, "#init declarations are not in normal expressions, only in #after clauses.");
+ }
+
+ 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_Intrinsic_Call:
+ 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:
+ YIELD_(expr->token->pos, "Waiting to resolve symbol, '%b'.", expr->token->text, expr->token->length);
+ break;
+
+ case Ast_Kind_Param:
+ if (expr->type == NULL) {
+ YIELD(expr->token->pos, "Waiting on parameter type.");
+ }
+ break;
+
+ case Ast_Kind_Local: break;
+
+ case Ast_Kind_Address_Of: retval = check_address_of((AstAddressOf **) pexpr); 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, Error_Critical, "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:
+ CHECK(directive_solidify, (AstDirectiveSolidify **) pexpr);
+ 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_Memres:
+ if (expr->type == NULL) YIELD(expr->token->pos, "Waiting to know globals type.");
+ 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_Polymorphic_Proc: break;
+ case Ast_Kind_Package: break;
+ case Ast_Kind_Error: break;
+ case Ast_Kind_Unary_Field_Access: break;
+ case Ast_Kind_Constraint_Sentinel: break;
+ case Ast_Kind_Switch_Case: break;
+ case Ast_Kind_Foreign_Block: break;
+ case Ast_Kind_Zero_Value: break;
+
+ default:
+ retval = Check_Error;
+ onyx_report_error(expr->token->pos, Error_Critical, "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);
+
+ TYPE_CHECK(&insert->code_expr, code_type) {
+ ERROR_(insert->token->pos, "#unquote 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_directive_solidify(AstDirectiveSolidify** psolid) {
+ AstDirectiveSolidify* solid = *psolid;
+
+ bh_arr_each(AstPolySolution, sln, solid->known_polyvars) {
+ CHECK(expression, &sln->value);
+
+ 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;
+ }
+ }
+
+ solid->resolved_proc = polymorphic_proc_try_solidify(solid->poly_proc, solid->known_polyvars, solid->token);
+ if (solid->resolved_proc == (AstNode *) &node_that_signals_a_yield) {
+ solid->resolved_proc = NULL;
+ YIELD(solid->token->pos, "Waiting for partially solidified procedure.");
+ }
+
+ // NOTE: Not a DirectiveSolidify.
+ *psolid = (AstDirectiveSolidify *) solid->resolved_proc;
+
+ return Check_Success;
+}
+
+CheckStatus check_remove_directive(AstDirectiveRemove *remove) {
+ if (!inside_for_iterator) {
+ ERROR(remove->token->pos, "#remove is only allowed in the body of a for-loop over an iterator.");
+ }
+
+ return Check_Success;
+}
+
+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_Directive_Remove: return check_remove_directive((AstDirectiveRemove *) stmt);
+ case Ast_Kind_Call: {
+ CHECK(call, (AstCall **) pstmt);
+ (*pstmt)->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.");
+ }
+
+ if (typed_stmt->next != NULL && typed_stmt->next->kind == Ast_Kind_Binary_Op) {
+ AstBinaryOp *next = (AstBinaryOp *) typed_stmt->next;
+ if (next->operation == Binary_Op_Assign && next->left == typed_stmt) {
+ typed_stmt->flags |= Ast_Flag_Decl_Followed_By_Init;
+ }
+ }
+ 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) {
+ // This used to use statement_chain, but since block optimize which statements need to be rechecked,
+ // it has to be its own thing.
+
+ AstNode** start = &block->body;
+ fori (i, 0, block->statement_idx) {
+ start = &(*start)->next;
+ }
+
+ while (*start) {
+ CheckStatus cs = check_statement(start);
+ switch (cs) {
+ case Check_Success:
+ start = &(*start)->next;
+ block->statement_idx++;
+ break;
+
+ case Check_Return_To_Symres:
+ block->statement_idx = 0;
+
+ default:
+ return cs;
+ }
+
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_function(AstFunction* func) {
+ if (func->flags & Ast_Flag_Has_Been_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");
+
+ bh_arr_each(AstTyped *, pexpr, func->tags) {
+ CHECK(expression, pexpr);
+
+ if (((*pexpr)->flags & Ast_Flag_Comptime) == 0) {
+ ERROR((*pexpr)->token->pos, "#tag expressions should be compile time known.");
+ }
+ }
+
+ inside_for_iterator = 0;
+ 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) {
+ expected_return_type = NULL;
+ return status;
+ }
+ }
+
+ if (*expected_return_type == &type_auto_return) {
+ *expected_return_type = &basic_types[Basic_Kind_Void];
+ }
+
+ func->flags |= Ast_Flag_Has_Been_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, Error_Critical, "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.");
+
+ if (s_node->min_size_) CHECK(expression, &s_node->min_size_);
+ if (s_node->min_alignment_) CHECK(expression, &s_node->min_alignment_);
+
+ if (s_node->polymorphic_argument_types) {
+ assert(s_node->polymorphic_arguments);
+
+ fori (i, 0, (i64) bh_arr_length(s_node->polymorphic_argument_types)) {
+ Type *arg_type = type_build_from_ast(context.ast_alloc, s_node->polymorphic_argument_types[i]);
+ if (arg_type == NULL) YIELD(s_node->polymorphic_argument_types[i]->token->pos, "Waiting to build type for polymorph argument.");
+
+ // CLEANUP: This might be wrong...
+ if (s_node->polymorphic_arguments[i].value) {
+ TYPE_CHECK(&s_node->polymorphic_arguments[i].value, arg_type) {
+ ERROR_(s_node->polymorphic_arguments[i].value->token->pos, "Expected value of type %s, got %s.",
+ type_get_name(arg_type),
+ type_get_name(s_node->polymorphic_arguments[i].value->type));
+ }
+ }
+ }
+ }
+
+ if (s_node->constraints.constraints) {
+ s_node->constraints.produce_errors = (s_node->flags & Ast_Flag_Header_Check_No_Error) == 0;
+
+ OnyxFilePos pos = s_node->token->pos;
+ if (s_node->polymorphic_error_loc.filename) {
+ pos = s_node->polymorphic_error_loc;
+ }
+ CHECK(constraint_context, &s_node->constraints, s_node->scope, pos);
+ }
+
+ //
+ // This ensures that all procedures defined inside of a structure are
+ // not pruned and omitted from the binary. This is because a large
+ // use-case of procedures in structures is dynamically linking them
+ // using the type info data.
+ if (s_node->scope) {
+ fori (i, 0, shlen(s_node->scope->symbols)) {
+ AstNode* node = s_node->scope->symbols[i].value;
+ if (node->kind == Ast_Kind_Function) {
+ node->flags |= Ast_Flag_Function_Used;
+ }
+ }
+ }
+
+ 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 pending_type.
+ s_node->ready_to_build_type = 1;
+ type_build_from_ast(context.ast_alloc, (AstType *) s_node);
+ if (s_node->pending_type == NULL || !s_node->pending_type_is_valid)
+ YIELD(s_node->token->pos, "Waiting for type to be constructed.");
+
+ bh_arr_each(StructMember *, smem, s_node->pending_type->Struct.memarr) {
+ if ((*smem)->type->kind == Type_Kind_Compound) {
+ ERROR(s_node->token->pos, "Compound types are not allowed as struct member types.");
+ }
+
+ if ((*smem)->used) {
+ if (!type_struct_member_apply_use(context.ast_alloc, s_node->pending_type, *smem)) {
+ YIELD((*smem)->token->pos, "Waiting for use to be applied.");
+ }
+ }
+ }
+
+ s_node->stcache = s_node->pending_type;
+ s_node->stcache->Struct.status = SPS_Uses_Done;
+
+ 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.");
+ if (s_node->entity_type && s_node->entity_type->state == Entity_State_Failed)
+ return Check_Failed;
+
+ if (s_node->meta_tags) {
+ bh_arr_each(AstTyped *, meta, s_node->meta_tags) {
+ CHECK(expression, meta);
+ resolve_expression_type(*meta);
+
+ if (((*meta)->flags & Ast_Flag_Comptime) == 0) {
+ onyx_report_error((*meta)->token->pos, Error_Critical, "#tag expressions are expected to be compile-time known.");
+ return Check_Error;
+ }
+ }
+ }
+
+ bh_arr_each(StructMember *, smem, s_node->stcache->Struct.memarr) {
+ if ((*smem)->initial_value && *(*smem)->initial_value) {
+ CHECK(expression, (*smem)->initial_value);
+
+ TYPE_CHECK((*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);
+ }
+
+ if ((*smem)->meta_tags) {
+ bh_arr_each(AstTyped *, meta, (*smem)->meta_tags) {
+ CHECK(expression, meta);
+ resolve_expression_type(*meta);
+
+ if (((*meta)->flags & Ast_Flag_Comptime) == 0) {
+ onyx_report_error((*meta)->token->pos, Error_Critical, "#tag expressions are expected to be compile-time known.");
+ return Check_Error;
+ }
+ }
+ }
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_temp_function_header(AstFunction* func) {
+ CheckStatus cs = check_function_header(func);
+ if (cs == Check_Error) {
+ if (func->flags & Ast_Flag_Header_Check_No_Error) {
+ onyx_clear_errors();
+ }
+
+ return Check_Failed;
+ }
+
+ if (cs != Check_Success) return cs;
+
+ return Check_Complete;
+}
+
+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;
+
+ if (func->constraints.constraints != NULL && func->constraints.constraints_met == 0) {
+ func->constraints.produce_errors = (func->flags & Ast_Flag_Header_Check_No_Error) == 0;
+ CHECK(constraint_context, &func->constraints, func->scope, func->token->pos);
+
+ // All constraints have been met. Return to symbol resolution to finish
+ // looking up all symbols in the function.
+ return Check_Return_To_Symres;
+ }
+
+ 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) {
+ // If the function has the no_error flag, then the type node should have it set too.
+ // This allows for polymorphic structures with constraints to fail gracefully.
+ local->type_node->flags |= (func->flags & Ast_Flag_Header_Check_No_Error);
+ CHECK(type, &local->type_node);
+ }
+
+ fill_in_type((AstTyped *) local);
+ if (local->type == NULL) {
+ YIELD(local->token->pos, "Waiting for parameter type to be known.");
+ }
+
+ if (local->type == (Type *) &node_that_signals_failure) {
+ return Check_Failed;
+ }
+
+ 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 (!unify_node_and_type(¶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) {
+ assert(memres->type_entity);
+ if (memres->type_entity->state < Entity_State_Code_Gen) YIELD(memres->token->pos, "Waiting for global to pass type construction.");
+
+ if (memres->initial_value != NULL) {
+ if (memres->threadlocal) {
+ onyx_report_error(memres->token->pos, Error_Critical, "'#thread_local' variables cannot have an initializer at the moment.");
+ return Check_Error;
+ }
+
+ CHECK(expression, &memres->initial_value);
+
+ if (memres->type != NULL) {
+ Type* memres_type = memres->type;
+ TYPE_CHECK(&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 {
+ resolve_expression_type(memres->initial_value);
+ 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;
+ }
+
+ if ((memres->initial_value->flags & Ast_Flag_Comptime) == 0) {
+ if (memres->initial_value->entity != NULL && memres->initial_value->entity->state <= Entity_State_Check_Types) {
+ YIELD(memres->token->pos, "Waiting for initial value to be checked.");
+ }
+
+ ERROR(memres->initial_value->token->pos, "Top level expressions must be compile time known.");
+ }
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_type(AstType** ptype) {
+ if (ptype == NULL || *ptype == NULL) return Check_Success;
+
+ AstType* type = *ptype;
+ AstType* original_type = type;
+ while (type->kind == Ast_Kind_Type_Alias)
+ type = ((AstTypeAlias *) type)->to;
+
+ if (type->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
+ 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);
+ fill_in_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: ((AstPointerType *) type)->elem->flags |= type->flags & Ast_Flag_Header_Check_No_Error; CHECK(type, &((AstPointerType *) type)->elem); break;
+ case Ast_Kind_Slice_Type: ((AstSliceType *) type)->elem->flags |= type->flags & Ast_Flag_Header_Check_No_Error; CHECK(type, &((AstSliceType *) type)->elem); break;
+ case Ast_Kind_DynArr_Type: ((AstDynArrType *) type)->elem->flags |= type->flags & Ast_Flag_Header_Check_No_Error; CHECK(type, &((AstDynArrType *) type)->elem); break;
+ case Ast_Kind_VarArg_Type: ((AstVarArgType *) type)->elem->flags |= type->flags & Ast_Flag_Header_Check_No_Error; 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;
+ }
+
+ case Ast_Kind_Array_Type: {
+ AstArrayType* atype = (AstArrayType *) type;
+ if (atype->count_expr) {
+ CHECK(expression, &atype->count_expr);
+ resolve_expression_type(atype->count_expr);
+ }
+
+ break;
+ }
+
+ case Ast_Kind_Field_Access: {
+ CHECK(field_access, (AstFieldAccess **) ptype);
+ type = *ptype;
+ original_type = type;
+
+ if (!node_is_type((AstNode *) type)) {
+ ERROR_(original_type->token->pos, "This field access did not resolve to be a type. It resolved to be a '%s'.", onyx_ast_node_kind_string(type->kind));
+ }
+ break;
+ }
+ }
+
+ type = original_type;
+ type->flags |= Ast_Flag_Comptime;
+ while (type->kind == Ast_Kind_Type_Alias) {
+ type->flags |= Ast_Flag_Comptime;
+ type = ((AstTypeAlias *) type)->to;
+ }
+
+ type->flags |= Ast_Flag_Has_Been_Checked;
+ return Check_Success;
+}
+
+CheckStatus check_static_if(AstIf* static_if) {
+ expression_types_must_be_known = 1;
+ CheckStatus result = check_expression(&static_if->cond);
+ expression_types_must_be_known = 0;
+ if (result == Check_Yield_Macro) return Check_Yield_Macro;
+
+ 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) {
+ AstDirectiveExport *export = (AstDirectiveExport *) directive;
+ AstTyped *exported = export->export;
+ if (exported->entity && exported->entity->state <= Entity_State_Check_Types)
+ YIELD(directive->token->pos, "Waiting for exported type to be known.");
+
+ CHECK(expression, &export->export_name_expr);
+
+ if (export->export_name_expr->kind != Ast_Kind_StrLit) {
+ ERROR_(export->token->pos, "Expected export name to be a string literal, got '%s'.", onyx_ast_node_kind_string(export->export_name_expr->kind));
+ }
+
+ export->export_name = export->export_name_expr->token;
+ }
+
+ if (directive->kind == Ast_Kind_Directive_Init) {
+ AstDirectiveInit *init = (AstDirectiveInit *) directive;
+ if ((init->flags & Ast_Flag_Has_Been_Checked) == 0) {
+ CHECK(expression, &init->init_proc);
+
+ if (init->init_proc->kind != Ast_Kind_Function) {
+ ERROR_(init->token->pos, "#init only works for functions, got '%s'", onyx_ast_node_kind_string(init->init_proc->kind));
+ }
+
+ assert(init->init_proc->type);
+ if (init->init_proc->type->Function.param_count != 0) {
+ ERROR(init->token->pos, "#init expects a function that takes 0 arguments.");
+ }
+ }
+
+ init->flags |= Ast_Flag_Has_Been_Checked;
+
+ if (init->dependencies) {
+ i32 i = 0;
+ bh_arr_each(AstDirectiveInit *, dependency, init->dependencies) {
+ AstTyped *d = (AstTyped *) strip_aliases((AstNode *) *dependency);
+ if (d->kind != Ast_Kind_Directive_Init) {
+ ERROR_(init->token->pos, "All dependencies of an #init must be another #init. The %d%s dependency was not.", i + 1, bh_num_suffix(i + 1));
+ }
+
+ assert(d->entity);
+ if (d->entity->state != Entity_State_Finalized) {
+ YIELD(init->token->pos, "Circular dependency in #init nodes. Here are the nodes involved.");
+ }
+
+ i++;
+ }
+ }
+
+ bh_arr_push(init_procedures, (AstFunction *) init->init_proc);
+ return Check_Complete;
+ }
+
+ if (directive->kind == Ast_Kind_Directive_Library) {
+ AstDirectiveLibrary *library = (AstDirectiveLibrary *) directive;
+
+ if (library->library_symbol->kind != Ast_Kind_StrLit) {
+ ERROR_(library->token->pos, "#library directive expected compile-time known string for library name. Got '%s'.",
+ onyx_ast_node_kind_string(library->library_symbol->kind));
+ }
+
+ AstStrLit *symbol = (AstStrLit *) library->library_symbol;
+ char* temp_name = bh_alloc_array(global_scratch_allocator, char, symbol->token->length);
+ i32 temp_name_len = string_process_escape_seqs(temp_name, symbol->token->text, symbol->token->length);
+ library->library_name = bh_strdup(global_heap_allocator, temp_name);
+ return Check_Success;
+ }
+
+ 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_constraint(AstConstraint *constraint) {
+ switch (constraint->phase) {
+ case Constraint_Phase_Cloning_Expressions: {
+ if (constraint->interface->kind == Ast_Kind_Symbol) {
+ return Check_Return_To_Symres;
+ }
+
+ if (constraint->interface->kind != Ast_Kind_Interface) {
+ // CLEANUP: This error message might not look totally right in some cases.
+ ERROR_(constraint->token->pos, "'%b' is not an interface. It is a '%s'.",
+ constraint->token->text, constraint->token->length,
+ onyx_ast_node_kind_string(constraint->interface->kind));
+ }
+
+ bh_arr_new(global_heap_allocator, constraint->exprs, bh_arr_length(constraint->interface->exprs));
+ bh_arr_each(InterfaceConstraint, ic, constraint->interface->exprs) {
+ InterfaceConstraint new_ic = {0};
+ new_ic.expr = (AstTyped *) ast_clone(context.ast_alloc, (AstNode *) ic->expr);
+ new_ic.expected_type_expr = (AstType *) ast_clone(context.ast_alloc, (AstNode *) ic->expected_type_expr);
+ new_ic.invert_condition = ic->invert_condition;
+ bh_arr_push(constraint->exprs, new_ic);
+ }
+
+ assert(constraint->interface->entity && constraint->interface->entity->scope);
+
+ constraint->scope = scope_create(context.ast_alloc, constraint->interface->entity->scope, constraint->token->pos);
+
+ fori (i, 0, bh_arr_length(constraint->interface->params)) {
+ InterfaceParam *ip = &constraint->interface->params[i];
+
+ AstTyped *sentinel = onyx_ast_node_new(context.ast_alloc, sizeof(AstTyped), Ast_Kind_Constraint_Sentinel);
+ sentinel->token = ip->value_token;
+ sentinel->type_node = constraint->type_args[i];
+
+ AstAlias *type_alias = onyx_ast_node_new(context.ast_alloc, sizeof(AstAlias), Ast_Kind_Alias);
+ type_alias->token = ip->type_token;
+ type_alias->alias = (AstTyped *) constraint->type_args[i];
+
+ symbol_introduce(constraint->scope, ip->value_token, (AstNode *) sentinel);
+ symbol_introduce(constraint->scope, ip->type_token, (AstNode *) type_alias);
+ }
+
+ assert(constraint->entity);
+ constraint->entity->scope = constraint->scope;
+
+ constraint->phase = Constraint_Phase_Checking_Expressions;
+ return Check_Return_To_Symres;
+ }
+
+ case Constraint_Phase_Checking_Expressions: {
+ fori (i, constraint->expr_idx, bh_arr_length(constraint->exprs)) {
+ InterfaceConstraint* ic = &constraint->exprs[i];
+
+ CheckStatus cs = check_expression(&ic->expr);
+ if (cs == Check_Return_To_Symres || cs == Check_Yield_Macro) {
+ return cs;
+ }
+
+ if (cs == Check_Error && !ic->invert_condition) {
+ goto constraint_error;
+ }
+
+ if (cs == Check_Success && ic->invert_condition) {
+ goto constraint_error;
+ }
+
+ if (ic->expected_type_expr) {
+ cs = check_type(&ic->expected_type_expr);
+ if (cs == Check_Return_To_Symres || cs == Check_Yield_Macro) {
+ return cs;
+ }
+
+ ic->expected_type = type_build_from_ast(context.ast_alloc, ic->expected_type_expr);
+ if (ic->expected_type == NULL) {
+ YIELD(ic->expected_type_expr->token->pos, "Waiting on expected type expression to be resolved.");
+ }
+
+ TYPE_CHECK(&ic->expr, ic->expected_type) {
+ if (!ic->invert_condition)
+ goto constraint_error;
+ }
+ }
+
+ constraint->expr_idx++;
+ continue;
+
+ constraint_error:
+ // HACK HACK HACK
+ onyx_clear_errors();
+ *constraint->report_status = Constraint_Check_Status_Failed;
+ return Check_Failed;
+ }
+
+ // HACK HACK HACK
+ onyx_clear_errors();
+ *constraint->report_status = Constraint_Check_Status_Success;
+ return Check_Complete;
+ }
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_constraint_context(ConstraintContext *cc, Scope *scope, OnyxFilePos pos) {
+ if (cc->constraint_checks) {
+ if (cc->constraints_met == 1) return Check_Success;
+
+ fori (i, 0, bh_arr_length(cc->constraints)) {
+ if (cc->constraint_checks[i] == Constraint_Check_Status_Failed) {
+ if (cc->produce_errors) {
+ AstConstraint *constraint = cc->constraints[i];
+ char constraint_map[512] = {0};
+ fori (i, 0, bh_arr_length(constraint->type_args)) {
+ if (i != 0) strncat(constraint_map, ", ", 511);
+
+ OnyxToken* symbol = constraint->interface->params[i].value_token;
+ token_toggle_end(symbol);
+ strncat(constraint_map, symbol->text, 511);
+ token_toggle_end(symbol);
+
+ strncat(constraint_map, " is of type '", 511);
+ strncat(constraint_map, type_get_name(type_build_from_ast(context.ast_alloc, constraint->type_args[i])), 511);
+ strncat(constraint_map, "'", 511);
+ }
+
+ onyx_report_error(constraint->exprs[constraint->expr_idx].expr->token->pos, Error_Critical, "Failed to satisfy constraint where %s.", constraint_map);
+ onyx_report_error(constraint->token->pos, Error_Critical, "Here is where the interface was used.");
+ onyx_report_error(pos, Error_Critical, "Here is the code that caused this constraint to be checked.");
+
+ return Check_Error;
+
+ } else {
+ // If no error are suppose to be produced, we still need to signal that
+ // the node reached a completed state.
+ return Check_Failed;
+ }
+ }
+
+ if (cc->constraint_checks[i] == Constraint_Check_Status_Queued) {
+ YIELD(pos, "Waiting for constraints to be checked.");
+ }
+ }
+
+ cc->constraints_met = 1;
+ return Check_Success;
+
+ } else {
+ u32 count = bh_arr_length(cc->constraints);
+ ConstraintCheckStatus *ccs = bh_alloc_array(context.ast_alloc, ConstraintCheckStatus, count);
+
+ cc->constraint_checks = ccs;
+
+ fori (i, 0, count) {
+ ccs[i] = Constraint_Check_Status_Queued;
+ cc->constraints[i]->report_status = &ccs[i];
+ cc->constraints[i]->phase = Constraint_Phase_Cloning_Expressions;
+
+ add_entities_for_node(NULL, (AstNode *) cc->constraints[i], scope, NULL);
+ }
+
+ return Check_Yield_Macro;
+ }
+}
+
+CheckStatus check_polyquery(AstPolyQuery *query) {
+ if (query->function_header->scope == NULL)
+ query->function_header->scope = scope_create(context.ast_alloc, query->proc->parent_scope_of_poly_proc, query->token->pos);
+
+ CheckStatus header_check = check_temp_function_header(query->function_header);
+ if (header_check == Check_Return_To_Symres) return Check_Return_To_Symres;
+
+ b32 solved_something = 0;
+ i32 solved_count = 0;
+ char *err_msg = NULL;
+ bh_arr_each(AstPolyParam, param, query->proc->poly_params) {
+ AstPolySolution sln;
+ bh_arr_each(AstPolySolution, solved_sln, query->slns) {
+ if (token_equals(param->poly_sym->token, solved_sln->poly_sym->token)) {
+ goto poly_query_done;
+ }
+ }
+
+ // CLEANUP: I think this can go away because it is already done in polymorph.c
+ bh_arr_each(AstPolySolution, known_sln, query->proc->known_slns) {
+ if (token_equals(param->poly_sym->token, known_sln->poly_sym->token)) {
+ sln = *known_sln;
+ goto poly_var_solved;
+ }
+ }
+
+ TypeMatch result = find_polymorphic_sln(&sln, param, query->function_header, query->pp_lookup, query->given, &err_msg);
+
+ switch (result) {
+ case TYPE_MATCH_SUCCESS:
+ goto poly_var_solved;
+
+ case TYPE_MATCH_SPECIAL:
+ return Check_Yield_Macro;
+
+ case TYPE_MATCH_YIELD:
+ case TYPE_MATCH_FAILED: {
+ if (query->successful_symres || solved_something) continue;
+
+ if (query->error_on_fail || context.cycle_detected) {
+ onyx_report_error(query->token->pos, Error_Critical, "Error solving for polymorphic variable '%b'.", param->poly_sym->token->text, param->poly_sym->token->length);
+ if (err_msg != NULL) onyx_report_error(query->token->pos, Error_Critical, "%s", err_msg);
+ if (query->error_loc) onyx_report_error(query->error_loc->pos, Error_Critical, "Here is where the call is located."); // :ErrorMessage
+ }
+
+ return Check_Failed;
+ }
+ }
+
+poly_var_solved:
+ solved_something = 1;
+ bh_arr_push(query->slns, sln);
+ insert_poly_sln_into_scope(query->function_header->scope, &sln);
+
+poly_query_done:
+ solved_count += 1;
+ }
+
+ if (solved_count != bh_arr_length(query->proc->poly_params)) {
+ if (solved_something || query->successful_symres) {
+ return Check_Return_To_Symres;
+ } else {
+ return Check_Failed;
+ }
+ }
+
+ return Check_Complete;
+}
+
+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_Temp_Function_Header: cs = check_temp_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); break;
+ case Entity_Type_Constraint_Check: cs = check_constraint(ent->constraint); break;
+ case Entity_Type_Polymorph_Query: cs = check_polyquery(ent->poly_query); break;
+ case Entity_Type_Enum_Value: cs = check_expression(&ent->enum_value->value); break;
+ case Entity_Type_Process_Directive: cs = check_process_directive((AstNode *) ent->expr); break;
+
+ 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_File_Contents:
+ if (context.options->no_file_contents) {
+ onyx_report_error(ent->expr->token->pos, Error_Critical, "#file_contents is disabled for this compilation.");
+ }
+ break;
+
+ default: break;
+ }
+
+ switch (cs) {
+ case Check_Yield_Macro: ent->macro_attempts++; break;
+ case Check_Success: ent->state = Entity_State_Code_Gen; goto clear_attempts;
+ case Check_Complete: ent->state = Entity_State_Finalized; goto clear_attempts;
+ case Check_Return_To_Symres: ent->state = Entity_State_Resolve_Symbols; goto clear_attempts;
+ case Check_Failed: ent->state = Entity_State_Failed; goto clear_attempts;
+
+ clear_attempts:
+ ent->macro_attempts = 0;
+ ent->micro_attempts = 0;
+ }
+}
--- /dev/null
+#include "astnodes.h"
+#include "parser.h"
+#include "utils.h"
+
+// Weird flags that shouldn't be used too often because they complicate things
+static b32 dont_copy_structs = 0;
+
+static inline b32 should_clone(AstNode* node) {
+ if (node->flags & Ast_Flag_No_Clone) return 0;
+
+ if (dont_copy_structs) {
+ if (node->kind == Ast_Kind_Struct_Type) return 0;
+ if (node->kind == Ast_Kind_Function) return 0;
+ if (node->kind == Ast_Kind_Polymorphic_Proc) 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_Overloaded_Function:
+ case Ast_Kind_Alias:
+ case Ast_Kind_Code_Block:
+ case Ast_Kind_Macro:
+ case Ast_Kind_Symbol:
+ case Ast_Kind_Poly_Struct_Type:
+ case Ast_Kind_Basic_Type:
+ case Ast_Kind_Enum_Type:
+ case Ast_Kind_Enum_Value:
+ 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_Load_All: 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(AstFunction);
+ 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_Directive_Defined: return sizeof(AstDirectiveDefined);
+ case Ast_Kind_Do_Block: return sizeof(AstDoBlock);
+ case Ast_Kind_Constraint: return sizeof(AstConstraint);
+ case Ast_Kind_Directive_Remove: return sizeof(AstDirectiveRemove);
+ 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;
+}
+
+static bh_arr(AstNode *) captured_entities=NULL;
+#define E(ent) do { \
+ assert(captured_entities); \
+ ent->entity = NULL; \
+ bh_arr_push(captured_entities, (AstNode *) ent); \
+ } while (0);
+
+
+#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:
+ C(AstCall, callee);
+ 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);
+ //fallthrough
+
+ case Ast_Kind_Static_If:
+ C(AstIfWhile, cond);
+
+ C(AstIfWhile, true_stmt);
+ C(AstIfWhile, false_stmt);
+
+ if (nn->kind == Ast_Kind_Static_If) {
+ ((AstIfWhile *) node)->flags |= Ast_Flag_Dead;
+ ((AstIfWhile *) node)->flags |= Ast_Flag_Static_If_Resolved;
+
+ ((AstIfWhile *) nn)->flags &= ~Ast_Flag_Dead;
+ ((AstIfWhile *) nn)->flags &= ~Ast_Flag_Static_If_Resolved;
+ E(nn);
+ }
+
+ break;
+
+ case Ast_Kind_Switch_Case: {
+ C(AstSwitchCase, block);
+
+ AstSwitchCase *dw = (AstSwitchCase *) nn;
+ AstSwitchCase *sw = (AstSwitchCase *) node;
+
+ dw->values = NULL;
+ bh_arr_new(global_heap_allocator, dw->values, bh_arr_length(sw->values));
+ bh_arr_each(AstTyped *, value, sw->values)
+ bh_arr_push(dw->values, (AstTyped *) ast_clone(a, *value));
+
+ break;
+ }
+
+ case Ast_Kind_Switch: {
+ AstSwitch* dw = (AstSwitch *) nn;
+ AstSwitch* sw = (AstSwitch *) node;
+
+ dw->initialization = ast_clone_list(a, sw->initialization);
+ C(AstSwitch, expr);
+
+
+ dw->cases = NULL;
+ C(AstSwitch, case_block);
+ 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->meta_tags = NULL;
+ bh_arr_new(global_heap_allocator, ds->meta_tags, bh_arr_length(ss->meta_tags));
+ bh_arr_each(AstTyped *, tag, ss->meta_tags) {
+ bh_arr_push(ds->meta_tags, (AstTyped *) ast_clone(a, *tag));
+ }
+
+ if (ss->constraints.constraints) {
+ memset(&ds->constraints, 0, sizeof(ConstraintContext));
+ bh_arr_new(global_heap_allocator, ds->constraints.constraints, bh_arr_length(ss->constraints.constraints));
+
+ bh_arr_each(AstConstraint *, constraint, ss->constraints.constraints) {
+ bh_arr_push(ds->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
+ }
+ }
+
+ ds->stcache = NULL;
+ break;
+ }
+
+ case Ast_Kind_Struct_Member: {
+ C(AstStructMember, type_node);
+ C(AstStructMember, initial_value);
+
+ AstStructMember *ds = (AstStructMember *) nn;
+ AstStructMember *ss = (AstStructMember *) node;
+
+ ds->meta_tags = NULL;
+ bh_arr_new(global_heap_allocator, ds->meta_tags, bh_arr_length(ss->meta_tags));
+ bh_arr_each(AstTyped *, tag, ss->meta_tags) {
+ bh_arr_push(ds->meta_tags, (AstTyped *) ast_clone(a, *tag));
+ }
+
+ 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:
+ case Ast_Kind_Polymorphic_Proc: {
+ if (clone_depth > 1) {
+ clone_depth--;
+ return node;
+ }
+
+ AstFunction* df = (AstFunction *) nn;
+ AstFunction* sf = (AstFunction *) node;
+
+ convert_polyproc_to_function(df);
+
+ if (sf->is_foreign) return node;
+ assert(df->scope == NULL);
+
+ df->nodes_that_need_entities_after_clone = NULL;
+ bh_arr_new(global_heap_allocator, df->nodes_that_need_entities_after_clone, 1);
+ captured_entities = df->nodes_that_need_entities_after_clone;
+
+ df->return_type = (AstType *) ast_clone(a, sf->return_type);
+ df->body = (AstBlock *) ast_clone(a, sf->body);
+
+ df->nodes_that_need_entities_after_clone = captured_entities;
+ captured_entities = NULL;
+
+ df->params = NULL;
+ bh_arr_new(context.ast_alloc, df->params, bh_arr_length(sf->params));
+
+ bh_arr_each(AstParam, param, sf->params) {
+ AstParam new_param = { 0 };
+
+ dont_copy_structs = 1;
+ new_param.local = (AstLocal *) ast_clone(a, param->local);
+ new_param.local->flags &= ~Ast_Flag_Param_Symbol_Dirty;
+ new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
+ new_param.use_processed = 0;
+ dont_copy_structs = 0;
+
+ new_param.vararg_kind = param->vararg_kind;
+ new_param.is_used = param->is_used;
+ bh_arr_push(df->params, new_param);
+ }
+
+ if (sf->constraints.constraints) {
+ memset(&df->constraints, 0, sizeof(ConstraintContext));
+ bh_arr_new(context.ast_alloc, df->constraints.constraints, bh_arr_length(sf->constraints.constraints));
+
+ bh_arr_each(AstConstraint *, constraint, sf->constraints.constraints) {
+ bh_arr_push(df->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
+ }
+ }
+
+ if (sf->tags) {
+ bh_arr_new(context.ast_alloc, df->tags, bh_arr_length(sf->tags));
+ bh_arr_each(AstTyped *, pexpr, sf->tags) {
+ bh_arr_push(df->tags, (AstTyped *) ast_clone(a, (AstNode *) *pexpr));
+ }
+ }
+
+ break;
+ }
+
+ case Ast_Kind_Constraint: {
+ C(AstConstraint, interface);
+
+ AstConstraint* dc = (AstConstraint *) nn;
+ AstConstraint* sc = (AstConstraint *) node;
+
+ dc->type_args = NULL;
+ bh_arr_new(global_heap_allocator, dc->type_args, bh_arr_length(sc->type_args));
+
+ bh_arr_each(AstType *, type_arg, sc->type_args) {
+ bh_arr_push(dc->type_args, (AstType *) ast_clone(a, (AstNode *) *type_arg));
+ }
+
+ dc->phase = Constraint_Phase_Waiting_To_Be_Queued;
+ 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 = (AstFunction *) 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_Directive_Defined:
+ C(AstDirectiveDefined, expr);
+ ((AstDirectiveDefined *) nn)->is_defined = 0;
+ 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;
+
+ case Ast_Kind_File_Contents:
+ C(AstFileContents, filename_expr);
+ E(nn);
+ break;
+ }
+
+ clone_depth--;
+ return nn;
+}
+
+#undef C
+
+AstFunction* clone_function_header(bh_allocator a, AstFunction* func) {
+ if (func->kind != Ast_Kind_Function && func->kind != Ast_Kind_Polymorphic_Proc) return NULL;
+
+ if (func->is_foreign) return func;
+
+ AstFunction* new_func = onyx_ast_node_new(a, sizeof(AstFunction), func->kind);
+ memmove(new_func, func, sizeof(AstFunction));
+ assert(new_func->scope == NULL);
+
+ convert_polyproc_to_function(new_func);
+
+ 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;
+
+ dont_copy_structs = 1;
+ new_param.local = (AstLocal *) ast_clone(a, param->local);
+ new_param.local->flags &= ~Ast_Flag_Param_Symbol_Dirty;
+ new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
+ new_param.use_processed = 0;
+ dont_copy_structs = 0;
+
+ new_param.vararg_kind = param->vararg_kind;
+ new_param.is_used = param->is_used;
+ bh_arr_push(new_func->params, new_param);
+ }
+
+ if (func->constraints.constraints) {
+ memset(&new_func->constraints, 0, sizeof(ConstraintContext));
+ bh_arr_new(global_heap_allocator, new_func->constraints.constraints, bh_arr_length(func->constraints.constraints));
+
+ bh_arr_each(AstConstraint *, constraint, func->constraints.constraints) {
+ bh_arr_push(new_func->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
+ }
+ }
+
+ 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_Polymorphic_Proc && source->kind != Ast_Kind_Function) return;
+
+ dest->nodes_that_need_entities_after_clone = NULL;
+ bh_arr_new(global_heap_allocator, dest->nodes_that_need_entities_after_clone, 1);
+ captured_entities = dest->nodes_that_need_entities_after_clone;
+
+ dest->body = (AstBlock *) ast_clone(a, source->body);
+
+ dest->nodes_that_need_entities_after_clone = captured_entities;
+ captured_entities = NULL;
+}
--- /dev/null
+#include "doc.h"
+#include "utils.h"
+#include "types.h"
+
+static i32 cmp_doc_entry(const void * a, const void * b) {
+ DocEntry* d1 = (DocEntry *) a;
+ DocEntry* d2 = (DocEntry *) b;
+
+ return strncmp(d1->def, d2->def, 1024);
+}
+
+static i32 cmp_doc_package(const void * a, const void * b) {
+ DocPackage* d1 = (DocPackage *) a;
+ DocPackage* d2 = (DocPackage *) b;
+
+ return strncmp(d1->name, d2->name, 1024);
+}
+
+static char* node_to_doc_def(const char* sym, AstNode *node, bh_allocator a) {
+ static char buf[1024];
+ memset(buf, 0, 1023);
+
+ strncat(buf, sym, 1023);
+ strncat(buf, " :: ", 1023);
+
+ switch (node->kind) {
+ case Ast_Kind_Function: {
+ strncat(buf, "(", 1023);
+
+ AstFunction *func = (AstFunction *) node;
+ bh_arr_each(AstParam, param, func->params) {
+ if (param != func->params)
+ strncat(buf, ", ", 1023);
+
+ token_toggle_end(param->local->token);
+ strncat(buf, param->local->token->text, 1023);
+ token_toggle_end(param->local->token);
+
+ strncat(buf, ": ", 1023);
+
+ strncat(buf, type_get_name(param->local->type), 1023);
+
+ if (param->default_value != NULL) {
+ strncat(buf, " = <default>", 1023);
+ }
+ }
+
+ strncat(buf, ") -> ", 1023);
+ strncat(buf, type_get_name(func->type->Function.return_type), 1023);
+
+ break;
+ }
+
+ case Ast_Kind_Struct_Type: {
+ strncat(buf, "struct { ", 1023);
+
+ AstStructType* st = (AstStructType *) node;
+ bh_arr_each(AstStructMember *, smem, st->members) {
+
+ token_toggle_end((*smem)->token);
+ strncat(buf, (*smem)->token->text, 1023);
+ token_toggle_end((*smem)->token);
+
+ strncat(buf, ": ", 1023);
+
+ strncat(buf, type_get_name((*smem)->type), 1023);
+
+ strncat(buf, "; ", 1023);
+ }
+
+ strncat(buf, "}", 1023);
+ break;
+ }
+
+ case Ast_Kind_Basic_Type:
+ case Ast_Kind_Array_Type:
+ case Ast_Kind_Type_Alias:
+ case Ast_Kind_Slice_Type:
+ case Ast_Kind_DynArr_Type: {
+ strncat(buf, type_get_name(type_build_from_ast(global_heap_allocator, (AstType *) node)), 1023);
+ break;
+ }
+
+ default: {
+ strncat(buf, "<unimplemented printing>", 1023);
+ }
+ }
+
+ return bh_strdup(a, buf);
+}
+
+static DocPackage doc_package_create(Package* p, bh_allocator a) {
+ DocPackage dp;
+ dp.name = p->name;
+ dp.public_entries = NULL;
+ dp.private_entries = NULL;
+
+ bh_arr_new(global_heap_allocator, dp.public_entries, 16);
+ bh_arr_new(global_heap_allocator, dp.private_entries, 16);
+
+ fori (i, 0, shlen(p->scope->symbols)) {
+ char *key = p->scope->symbols[i].key;
+ AstNode *value = p->scope->symbols[i].value;
+
+ 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);
+ }
+
+ fori (i, 0, shlen(p->private_scope->symbols)) {
+ char *key = p->scope->symbols[i].key;
+ AstNode *value = p->scope->symbols[i].value;
+
+ 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);
+ }
+
+ 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);
+
+ fori (i, 0, shlen(context.packages)) {
+ DocPackage dp = doc_package_create(context.packages[i].value, a);
+ bh_arr_push(doc.package_docs, dp);
+ }
+
+ qsort(doc.package_docs, bh_arr_length(doc.package_docs), sizeof(DocPackage), cmp_doc_package);
+
+ return doc;
+}
+
+static void onyx_docs_emit_human(OnyxDocumentation* doc, bh_file* file) {
+ // NOTE: Disabling fancy line printing until I can make it better
+ #if 0
+ bh_arr_each(DocPackage, dp, doc->package_docs) {
+ bh_printf("Package '%s'\n", dp->name);
+
+ if (bh_arr_length(dp->public_entries) > 0) {
+ bh_printf("\xe2\x94\x9c\xe2\x94\x80 Public symbols\n");
+ bh_arr_each(DocEntry, de, dp->public_entries) {
+ bh_printf("\xe2\x94\x82 \xe2\x94\x9c\xe2\x94\x80 %s\n", de->def);
+
+ if (de->pos.filename != NULL)
+ bh_printf("\xe2\x94\x82 \xe2\x94\x82 at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
+ else
+ bh_printf("\xe2\x94\x82 \xe2\x94\x82 compiler built-in\n");
+
+ bh_printf("\xe2\x94\x82 \xe2\x94\x82\n");
+ }
+ }
+
+ if (bh_arr_length(dp->private_entries) > 0) {
+ bh_printf("\xe2\x94\x9c\xe2\x94\x80 Private symbols\n");
+ bh_arr_each(DocEntry, de, dp->private_entries) {
+ bh_printf("\xe2\x94\x82 \xe2\x94\x9c\xe2\x94\x80 %s\n", de->def);
+ if (de->pos.filename != NULL)
+ bh_printf("\xe2\x94\x82 \xe2\x94\x82 at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
+ else
+ bh_printf("\xe2\x94\x82 \xe2\x94\x82 compiler built-in\n");
+
+ bh_printf("\xe2\x94\x82 \xe2\x94\x82\n");
+ }
+
+ bh_printf("\n");
+ }
+ }
+ #else
+ bh_arr_each(DocPackage, dp, doc->package_docs) {
+ bh_fprintf(file, "Package '%s'\n", dp->name);
+
+ if (bh_arr_length(dp->public_entries) > 0) {
+ bh_fprintf(file, " Public symbols\n");
+ bh_arr_each(DocEntry, de, dp->public_entries) {
+ bh_fprintf(file, " %s\n", de->def);
+
+ if (de->pos.filename != NULL)
+ bh_fprintf(file, " at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
+ else
+ bh_fprintf(file, " compiler built-in\n");
+
+ bh_fprintf(file, " \n");
+ }
+ }
+
+ if (bh_arr_length(dp->private_entries) > 0) {
+ bh_fprintf(file, " Private symbols\n");
+ bh_arr_each(DocEntry, de, dp->private_entries) {
+ bh_fprintf(file, " %s\n", de->def);
+ if (de->pos.filename != NULL)
+ bh_fprintf(file, " at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
+ else
+ bh_fprintf(file, " compiler built-in\n");
+
+ bh_fprintf(file, " \n");
+ }
+
+ bh_fprintf(file, "\n");
+ }
+ }
+ #endif
+}
+
+
+static i32 sort_tags(const void* a, const void* b) {
+ DocEntry *da = *((DocEntry **) a);
+ DocEntry *db = *((DocEntry **) b);
+
+ return strcmp(da->sym, db->sym);
+}
+
+static void onyx_docs_emit_tags(OnyxDocumentation* doc) {
+ bh_file tags_file;
+ if (bh_file_create(&tags_file, "./tags") != BH_FILE_ERROR_NONE) {
+ bh_printf("Cannot create 'tags'.\n");
+ return;
+ }
+
+ bh_fprintf(&tags_file, "!_TAG_FILE_FORMAT\t2\n");
+ bh_fprintf(&tags_file, "!_TAG_FILE_SORTED\t1\n");
+ bh_fprintf(&tags_file, "!_TAG_OUTPUT_FILESEP\tslash\n");
+ bh_fprintf(&tags_file, "!_TAG_OUTPUT_MODE\tu-ctags\n");
+ bh_fprintf(&tags_file, "!_TAG_PROGRAM_AUTHOR\tOnyx Compiler\n");
+ bh_fprintf(&tags_file, "!_TAG_PROGRAM_NAME\tOnyx Compiler\n");
+ bh_fprintf(&tags_file, "!_TAG_PROGRAM_URL\thttps://github.com/brendanfh/onyx\n");
+ bh_fprintf(&tags_file, "!_TAG_PROGRAM_VERSION\t0.0.1\n");
+
+ bh_arr(DocEntry *) tags = NULL;
+ bh_arr_new(global_heap_allocator, tags, 256);
+
+ bh_arr_each(DocPackage, dp, doc->package_docs) {
+ bh_arr_each(DocEntry, de, dp->public_entries) bh_arr_push(tags, de);
+ bh_arr_each(DocEntry, de, dp->private_entries) bh_arr_push(tags, de);
+ }
+
+ qsort(tags, bh_arr_length(tags), sizeof(DocEntry *), sort_tags);
+
+ bh_arr_each(DocEntry *, tag, tags) {
+ if ((*tag)->pos.filename == NULL) continue;
+
+ i32 line_len = 0;
+ char *c = (*tag)->pos.line_start;
+ while (*c++ != '\n') line_len++;
+
+ bh_fprintf(&tags_file, "%s\t%s\t/^%b$/\n",
+ (*tag)->sym, (*tag)->pos.filename,
+ (*tag)->pos.line_start, line_len);
+ }
+
+ bh_file_close(&tags_file);
+}
+
+static void onyx_docs_emit_html(OnyxDocumentation* doc, bh_file* file) {
+ bh_fprintf(file, "HTML documentation output not supported yet.\n");
+ return;
+}
+
+void onyx_docs_emit(OnyxDocumentation* doc, const char* filename) {
+ bh_file file;
+ if (bh_file_create(&file, filename) != BH_FILE_ERROR_NONE) {
+ bh_printf("ERROR: Failed to open file '%s' for writing documentation.\n", filename);
+ return;
+ }
+
+ switch (doc->format) {
+ case Doc_Format_Human: onyx_docs_emit_human(doc, &file); break;
+ case Doc_Format_Html: onyx_docs_emit_html(doc, &file); break;
+ case Doc_Format_Tags: onyx_docs_emit_tags(doc); break;
+ }
+
+ bh_file_close(&file);
+}
+
--- /dev/null
+#include "bh.h"
+#include "astnodes.h"
+#include "utils.h"
+
+static inline i32 entity_phase(Entity* e1) {
+ if (e1->state <= Entity_State_Parse && e1->macro_attempts == 0) 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) {
+ while (1) {
+ 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;
+
+ index = min_index;
+ continue;
+ }
+
+ break;
+ }
+}
+
+void entity_heap_init(EntityHeap* entities) {
+ bh_arena_init(&entities->entity_arena, global_heap_allocator, 32 * 1024);
+}
+
+static u32 next_entity_id = 0;
+
+// 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->id = next_entity_id++;
+ 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);
+}
+
+void entity_change_type(EntityHeap* entities, Entity *ent, EntityType new_type) {
+ entities->type_count[ent->type]--;
+ entities->type_count[new_type]++;
+ ent->type = new_type;
+}
+
+// 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); \
+ } \
+
+ if (node->entity != NULL) return;
+
+ 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_All:
+ case Ast_Kind_Load_File: {
+ ent.type = Entity_Type_Load_File;
+ ent.include = (AstInclude *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Library_Path:
+ case Ast_Kind_Load_Path: {
+ 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 (((AstFunction *) node)->is_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: {
+ 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_Enum_Value: {
+ ent.type = Entity_Type_Enum_Value;
+ ent.state = Entity_State_Check_Types;
+ ent.enum_value = (AstEnumValue *) 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);
+ ((AstMemRes *) node)->type_entity = entity;
+
+ 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 = (AstFunction *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Polymorph_Query: {
+ ent.type = Entity_Type_Polymorph_Query;
+ ent.state = Entity_State_Check_Types;
+ ent.poly_query = (AstPolyQuery *) 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:
+ case Ast_Kind_Directive_Init:
+ case Ast_Kind_Directive_Library:
+ case Ast_Kind_Injection: {
+ 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;
+ }
+
+ case Ast_Kind_Interface: {
+ ent.type = Entity_Type_Interface;
+ ent.interface = (AstInterface *) node;
+ ent.state = Entity_State_Resolve_Symbols;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Constraint: {
+ ent.type = Entity_Type_Constraint_Check;
+ ent.constraint = (AstConstraint *) node;
+ ent.state = Entity_State_Resolve_Symbols;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Foreign_Block: {
+ ent.type = Entity_Type_Foreign_Block;
+ ent.foreign_block = (AstForeignBlock *) node;
+ ent.state = Entity_State_Resolve_Symbols;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ default: {
+ ent.type = Entity_Type_Expression;
+ ent.expr = (AstTyped *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+ }
+
+ node->entity = entity;
+}
--- /dev/null
+#include "errors.h"
+#include "utils.h"
+
+OnyxErrors errors;
+
+void onyx_errors_init(bh_arr(bh_file_contents)* files) {
+ errors.file_contents = files;
+
+ bh_arena_init(&errors.msg_arena, global_heap_allocator, 16 * 1024);
+ errors.msg_alloc = bh_arena_allocator(&errors.msg_arena);
+
+ bh_arr_new(global_heap_allocator, errors.errors, 4);
+}
+
+static void print_detailed_message(OnyxError* err, bh_file_contents* fc) {
+ bh_printf("(%s:%l,%l) %s\n", err->pos.filename, err->pos.line, err->pos.column, err->text);
+
+ b32 colored_printing = 0;
+ #ifdef _BH_LINUX
+ colored_printing = !context.options->no_colors;
+ #endif
+
+ i32 linelength = 0;
+ i32 first_char = 0;
+ char* walker = err->pos.line_start;
+ while (*walker == ' ' || *walker == '\t') first_char++, linelength++, walker++;
+ while (*walker != '\n') linelength++, walker++;
+
+ if (colored_printing) bh_printf("\033[90m");
+ i32 numlen = bh_printf(" %d | ", err->pos.line);
+ if (colored_printing) bh_printf("\033[94m");
+ bh_printf("%b\n", err->pos.line_start, linelength);
+
+ char* pointer_str = bh_alloc_array(global_scratch_allocator, char, linelength + numlen);
+ memset(pointer_str, ' ', linelength + 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[0m");
+}
+
+static i32 errors_sort(const void* v1, const void* v2) {
+ OnyxError* e1 = (OnyxError *) v1;
+ OnyxError* e2 = (OnyxError *) v2;
+ return e2->rank - e1->rank;
+}
+
+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
+
+ qsort(errors.errors, bh_arr_length(errors.errors), sizeof(OnyxError), errors_sort);
+
+ OnyxErrorRank last_rank = errors.errors[0].rank;
+ bh_arr_each(OnyxError, err, errors.errors) {
+ if (last_rank != err->rank) break;
+
+ 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);
+ }
+
+ last_rank = err->rank;
+ }
+}
+
+b32 onyx_has_errors() {
+ return bh_arr_length(errors.errors) > 0;
+}
+
+void onyx_clear_errors() {
+ if (context.cycle_detected) return;
+
+ bh_arr_set_length(errors.errors, 0);
+}
+
+void onyx_submit_error(OnyxError error) {
+ bh_arr_push(errors.errors, error);
+}
+
+void onyx_report_error(OnyxFilePos pos, OnyxErrorRank rank, char * format, ...) {
+
+ va_list vargs;
+ va_start(vargs, format);
+ char* msg = bh_bprintf_va(format, vargs);
+ va_end(vargs);
+
+ OnyxError err = {
+ .pos = pos,
+ .rank = rank,
+ .text = bh_strdup(errors.msg_alloc, msg),
+ };
+
+ bh_arr_push(errors.errors, err);
+}
+
+void onyx_submit_warning(OnyxError error) {
+ bh_file_contents file_contents = { 0 };
+ bh_arr_each(bh_file_contents, fc, *errors.file_contents) {
+ if (!strcmp(fc->filename, error.pos.filename)) {
+ file_contents = *fc;
+ break;
+ }
+ }
+
+ print_detailed_message(&error, &file_contents);
+}
+
+// This definitely doesn't do what I thought it did?
+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,
+ .rank = Error_Warning,
+ .text = msg,
+ };
+
+ bh_arr_push(errors.errors, err);
+
+ /*
+
+ bh_file_contents file_contents = { 0 };
+ bh_arr_each(bh_file_contents, fc, *errors.file_contents) {
+ if (!strcmp(fc->filename, pos.filename)) {
+ file_contents = *fc;
+ break;
+ }
+ }
+
+ print_detailed_message(&err, &file_contents);
+ */
+}
--- /dev/null
+#include "bh.h"
+#include "lex.h"
+#include "utils.h"
+#include "errors.h"
+
+u64 lexer_lines_processed = 0;
+u64 lexer_tokens_processed = 0;
+
+static const char* token_type_names[] = {
+ "TOKEN_TYPE_UNKNOWN",
+ "TOKEN_TYPE_END_STREAM",
+
+ "TOKEN_TYPE_COMMENT",
+
+ "package",
+ "struct",
+ "enum",
+ "use",
+ "if",
+ "else",
+ "elseif",
+ "return",
+ "global",
+ "cast",
+ "while",
+ "for",
+ "break",
+ "continue",
+ "sizeof",
+ "alignof",
+ "typeof",
+ "defer",
+ "do",
+ "case",
+ "switch",
+ "fallthrough",
+ "macro",
+ "interface",
+ "where",
+
+ "->",
+ "<-",
+ "---",
+ "|>",
+
+ ">=",
+ "<=",
+ "==",
+ "!=",
+ "+=",
+ "-=",
+ "*=",
+ "/=",
+ "%=",
+ "&=",
+ "|=",
+ "^=",
+ "&&",
+ "||",
+ "<<",
+ ">>",
+ ">>>",
+ "<<=",
+ ">>=",
+ ">>>=",
+ "..",
+ "~~",
+
+ "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;
+ }
+
+ // She-bang
+ if (tokenizer->curr == tokenizer->start) {
+ if (*tokenizer->curr == '#' && *(tokenizer->curr + 1) == '!') {
+ tk.type = Token_Type_Comment;
+
+ tokenizer->curr += 2;
+ while (*tokenizer->curr++ != '\n' && tokenizer->curr != tokenizer->end);
+
+ tk.length = tokenizer->curr - tk.text;
+ 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;
+ }
+
+ if (*tk.text == '"' && *(tk.text + 1) == '"' && *(tk.text + 2) == '"') {
+ u64 len = 0;
+
+ INCREMENT_CURR_TOKEN(tokenizer);
+ INCREMENT_CURR_TOKEN(tokenizer);
+ INCREMENT_CURR_TOKEN(tokenizer);
+
+ while (!(*tokenizer->curr == '"' && *(tokenizer->curr + 1) == '"' && *(tokenizer->curr + 2) == '"')) {
+ len++;
+ INCREMENT_CURR_TOKEN(tokenizer);
+ }
+
+ INCREMENT_CURR_TOKEN(tokenizer);
+ INCREMENT_CURR_TOKEN(tokenizer);
+ INCREMENT_CURR_TOKEN(tokenizer);
+
+ tk.text += 3;
+ tk.length = len;
+ tk.type = Token_Type_Literal_String;
+ 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);
+ 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);
+ LITERAL_TOKEN("interface", 1, Token_Type_Keyword_Interface);
+ break;
+ case 'm':
+ LITERAL_TOKEN("macro", 1, Token_Type_Keyword_Macro);
+ break;
+ case 'p':
+ LITERAL_TOKEN("package", 1, Token_Type_Keyword_Package);
+ 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);
+ LITERAL_TOKEN("where", 1, Token_Type_Keyword_Where);
+ 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_Less_Equal);
+ LITERAL_TOKEN("<-", 0, Token_Type_Right_Arrow);
+ LITERAL_TOKEN("<<=", 0, Token_Type_Shl_Equal);
+ LITERAL_TOKEN("<<", 0, Token_Type_Shift_Left);
+ break;
+
+ case '>':
+ LITERAL_TOKEN(">=", 0, Token_Type_Greater_Equal);
+ 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);
+ 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) || *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;
+}
+
+b32 token_same_file(OnyxToken *tkn1, OnyxToken *tkn2) {
+ if (!tkn1 || !tkn2) return 0;
+
+ if (tkn1->pos.filename == tkn2->pos.filename) return 1;
+
+ // :Security?
+ return strcmp(tkn1->pos.filename, tkn2->pos.filename) == 0;
+}
--- /dev/null
+// #define BH_DEBUG
+#define BH_DEFINE
+#define BH_NO_TABLE
+#define STB_DS_IMPLEMENTATION
+#include "bh.h"
+
+#include "lex.h"
+#include "errors.h"
+#include "parser.h"
+#include "utils.h"
+#include "wasm_emit.h"
+#include "doc.h"
+
+#define VERSION "v0.1.0"
+
+
+// #ifndef CORE_INSTALLATION
+// #ifdef _BH_LINUX
+// #define CORE_INSTALLATION "/usr/share/onyx"
+// #elif defined(_WIN32) || defined(_WIN64)
+// #define CORE_INSTALLATION "\\dev\\onyx\\"
+// #endif
+// #endif
+
+
+
+
+Context context;
+
+
+static const char* docstring = "Onyx compiler version " VERSION "\n"
+ "\n"
+ "The compiler for the Onyx programming language, created by Brendan Hansen.\n"
+ "\n"
+ "Usage:\n"
+ "\tonyx compile [-o <target file>] [--verbose] <input files>\n"
+#ifdef ENABLE_RUN_WITH_WASMER
+ "\tonyx run <input files> -- <args>\n"
+#endif
+ // "\tonyx doc <input files>\n"
+ "\tonyx help\n"
+ "\n"
+ "Flags:\n"
+ "\t<input files> List of initial files\n"
+ "\t-o <target_file> Specify the target file (default: out.wasm).\n"
+ "\t--runtime, -r <runtime> Specifies the runtime. Can be: onyx, wasi, js, custom.\n"
+ "\t--verbose, -V Verbose output.\n"
+ "\t -VV Very verbose output.\n"
+ "\t -VVV Very very verbose output (to be used by compiler developers).\n"
+ "\t--wasm-mvp Use only WebAssembly MVP features.\n"
+ "\t--multi-threaded Enables multi-threading for this compilation.\n"
+ "\t--doc <doc_file>\n"
+ "\t--generate-foreign-info\n"
+ "\n"
+ "Developer flags:\n"
+ "\t--print-function-mappings Prints a mapping from WASM function index to source location.\n"
+ "\t--print-static-if-results Prints the conditional result of each #if statement. Useful for debugging.\n"
+ "\t--print-notes Prints the location of notes throughout the loaded code.\n"
+ "\t--no-colors Disables colors in the error message.\n"
+ "\t--no-file-contents Disables '#file_contents' for security.\n"
+ "\n";
+
+
+static CompileOptions compile_opts_parse(bh_allocator alloc, int argc, char *argv[]) {
+ CompileOptions options = {
+ .allocator = alloc,
+ .action = ONYX_COMPILE_ACTION_PRINT_HELP,
+
+ .verbose_output = 0,
+ .fun_output = 0,
+ .print_function_mappings = 0,
+ .no_file_contents = 0,
+
+ .use_post_mvp_features = 1,
+ .use_multi_threading = 0,
+
+ .runtime = Runtime_Onyx,
+
+ .files = NULL,
+ .target_file = "out.wasm",
+
+ .documentation_file = NULL,
+
+ .debug_enabled = 0,
+
+ .passthrough_argument_count = 0,
+ .passthrough_argument_data = NULL,
+ };
+
+ bh_arr_new(alloc, options.files, 2);
+ bh_arr_new(alloc, options.included_folders, 2);
+
+ char* core_installation;
+ #ifdef _BH_LINUX
+ core_installation = CORE_INSTALLATION;
+ #endif
+ #ifdef _BH_WINDOWS
+ core_installation = bh_alloc_array(global_heap_allocator, u8, 512);
+ GetEnvironmentVariableA("ONYX_PATH", core_installation, 512);
+ #endif
+
+ // NOTE: Add the current folder
+ bh_arr_push(options.included_folders, core_installation);
+ bh_arr_push(options.included_folders, ".");
+
+ if (argc == 1) return options;
+ i32 arg_parse_start = 1;
+
+ if (!strcmp(argv[1], "help")) options.action = ONYX_COMPILE_ACTION_PRINT_HELP;
+ if (!strcmp(argv[1], "compile")) {
+ options.action = ONYX_COMPILE_ACTION_COMPILE;
+ arg_parse_start = 2;
+ }
+ #ifdef ENABLE_RUN_WITH_WASMER
+ else if (!strcmp(argv[1], "run")) {
+ options.action = ONYX_COMPILE_ACTION_RUN;
+ arg_parse_start = 2;
+ }
+ #endif
+ else options.action = ONYX_COMPILE_ACTION_COMPILE;
+
+ if (options.action != ONYX_COMPILE_ACTION_PRINT_HELP) {
+ fori(i, arg_parse_start, argc) {
+ if (!strcmp(argv[i], "-o")) {
+ options.target_file = argv[++i];
+ }
+ else if (!strcmp(argv[i], "--verbose") || !strcmp(argv[i], "-V")) {
+ options.verbose_output = 1;
+ }
+ else if (!strcmp(argv[i], "-VV")) {
+ options.verbose_output = 2;
+ }
+ else if (!strcmp(argv[i], "-VVV")) {
+ options.verbose_output = 3;
+ }
+ else if (!strcmp(argv[i], "--print-function-mappings")) {
+ options.print_function_mappings = 1;
+ }
+ else if (!strcmp(argv[i], "--print-static-if-results")) {
+ options.print_static_if_results = 1;
+ }
+ else if (!strcmp(argv[i], "--print-notes")) {
+ options.print_notes = 1;
+ }
+ else if (!strcmp(argv[i], "--no-colors")) {
+ options.no_colors = 1;
+ }
+ else if (!strcmp(argv[i], "--no-file-contents")) {
+ options.no_file_contents = 1;
+ }
+ else if (!strcmp(argv[i], "--wasm-mvp")) {
+ options.use_post_mvp_features = 0;
+ }
+ else if (!strcmp(argv[i], "--multi-threaded")) {
+ options.use_multi_threading = 1;
+ }
+ else if (!strcmp(argv[i], "--generate-foreign-info")) {
+ options.generate_foreign_info = 1;
+ }
+ else if (!strcmp(argv[i], "-I")) {
+ bh_arr_push(options.included_folders, argv[++i]);
+ }
+ else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--runtime")) {
+ i += 1;
+ if (!strcmp(argv[i], "onyx")) options.runtime = Runtime_Onyx;
+ else if (!strcmp(argv[i], "wasi")) options.runtime = Runtime_Wasi;
+ else if (!strcmp(argv[i], "js")) options.runtime = Runtime_Js;
+ else if (!strcmp(argv[i], "custom")) options.runtime = Runtime_Custom;
+ else {
+ bh_printf("WARNING: '%s' is not a valid runtime. Defaulting to 'onyx'.\n", argv[i]);
+ options.runtime = Runtime_Wasi;
+ }
+ }
+ else if (!strcmp(argv[i], "--doc")) {
+ options.documentation_file = argv[++i];
+ }
+ else if (!strcmp(argv[i], "--debug")) {
+ options.debug_enabled = 1;
+ }
+ else if (!strcmp(argv[i], "--")) {
+ options.passthrough_argument_count = argc - i - 1;
+ options.passthrough_argument_data = &argv[i + 1];
+ break;
+ }
+#if defined(_BH_LINUX)
+ // NOTE: Fun output is only enabled for Linux because Windows command line
+ // is not ANSI compatible and for a silly feature, I don't want to learn
+ // how to properly do arbitrary graphics in it.
+ else if (!strcmp(argv[i], "--fun") || !strcmp(argv[i], "-F")) {
+ options.fun_output = 1;
+ }
+#endif
+ else {
+ bh_arr_push(options.files, argv[i]);
+ }
+ }
+ }
+
+ // NOTE: Always enable multi-threading for the Onyx runtime.
+ if (options.runtime == Runtime_Onyx) {
+ options.use_multi_threading = 1;
+ }
+
+ return options;
+}
+
+static void compile_opts_free(CompileOptions* opts) {
+ bh_arr_free(opts->files);
+ bh_arr_free(opts->included_folders);
+}
+
+
+
+
+
+typedef enum CompilerProgress {
+ ONYX_COMPILER_PROGRESS_ERROR,
+ ONYX_COMPILER_PROGRESS_FAILED_OUTPUT,
+ ONYX_COMPILER_PROGRESS_SUCCESS
+} CompilerProgress;
+
+static OnyxToken implicit_load_token = { '#', 1, 0, { 0, 0, 0, 0, 0 } };
+static AstInclude* create_load(bh_allocator alloc, char* filename) {
+ AstInclude* include_node = onyx_ast_node_new(alloc, sizeof(AstInclude), Ast_Kind_Load_File);
+ include_node->name = filename;
+ include_node->token = &implicit_load_token;
+
+ return include_node;
+}
+
+// HACK
+static u32 special_global_entities_remaining = 3;
+static Entity *runtime_info_types_entity;
+static Entity *runtime_info_foreign_entity;
+static Entity *runtime_info_proc_tags_entity;
+
+static void context_init(CompileOptions* opts) {
+ types_init();
+
+ context.options = opts;
+ context.cycle_detected = 0;
+ context.cycle_almost_detected = 0;
+
+ OnyxFilePos internal_location = { 0 };
+ internal_location.filename = "<compiler internal>";
+ internal_location.line = 1;
+ internal_location.column = 1;
+ context.global_scope = scope_create(global_heap_allocator, NULL, internal_location);
+ sh_new_arena(context.packages);
+
+ // NOTE: This will be initialized upon the first call to entity_heap_insert.
+ context.entities.next_id = 0;
+ context.entities.entities = NULL;
+
+ onyx_errors_init(&context.loaded_files);
+
+ context.token_alloc = global_heap_allocator;
+
+ // NOTE: Create the arena where tokens and AST nodes will exist
+ // Prevents nodes from being scattered across memory due to fragmentation
+ bh_arena_init(&context.ast_arena, global_heap_allocator, 16 * 1024 * 1024); // 16MB
+ context.ast_alloc = bh_arena_allocator(&context.ast_arena);
+
+ context.wasm_module = bh_alloc_item(global_heap_allocator, OnyxWasmModule);
+ *context.wasm_module = onyx_wasm_module_create(global_heap_allocator);
+
+ entity_heap_init(&context.entities);
+
+ // NOTE: Add builtin entities to pipeline.
+ entity_heap_insert(&context.entities, ((Entity) {
+ .state = Entity_State_Parse_Builtin,
+ .type = Entity_Type_Load_File,
+ .package = NULL,
+ .include = create_load(context.ast_alloc, "core/builtin"),
+ }));
+
+ entity_heap_insert(&context.entities, ((Entity) {
+ .state = Entity_State_Parse_Builtin,
+ .type = Entity_Type_Load_File,
+ .package = NULL,
+ .include = create_load(context.ast_alloc, "core/runtime/build_opts"),
+ }));
+
+ if (context.options->runtime != Runtime_Custom) {
+ runtime_info_types_entity = entity_heap_insert(&context.entities, ((Entity) {
+ .state = Entity_State_Parse,
+ .type = Entity_Type_Load_File,
+ .package = NULL,
+ .include = create_load(context.ast_alloc, "core/runtime/info/types"),
+ }));
+ runtime_info_foreign_entity = entity_heap_insert(&context.entities, ((Entity) {
+ .state = Entity_State_Parse,
+ .type = Entity_Type_Load_File,
+ .package = NULL,
+ .include = create_load(context.ast_alloc, "core/runtime/info/foreign_blocks"),
+ }));
+ runtime_info_proc_tags_entity = entity_heap_insert(&context.entities, ((Entity) {
+ .state = Entity_State_Parse,
+ .type = Entity_Type_Load_File,
+ .package = NULL,
+ .include = create_load(context.ast_alloc, "core/runtime/info/proc_tags"),
+ }));
+ }
+
+ add_entities_for_node(NULL, (AstNode *) &builtin_stack_top, context.global_scope, NULL);
+ add_entities_for_node(NULL, (AstNode *) &builtin_heap_start, context.global_scope, NULL);
+ add_entities_for_node(NULL, (AstNode *) &builtin_tls_base, context.global_scope, NULL);
+ add_entities_for_node(NULL, (AstNode *) &builtin_tls_size, context.global_scope, NULL);
+
+ // NOTE: Add all files passed by command line to the queue
+ bh_arr_each(const char *, filename, opts->files) {
+ AstInclude* load_node = create_load(context.ast_alloc, (char *) *filename);
+ add_entities_for_node(NULL, (AstNode *) load_node, context.global_scope, NULL);
+ }
+}
+
+static void context_free() {
+ bh_arena_free(&context.ast_arena);
+ bh_arr_free(context.loaded_files);
+
+ compile_opts_free(context.options);
+}
+
+static void parse_source_file(bh_file_contents* file_contents) {
+ // :Remove passing the allocators as parameters
+ OnyxTokenizer tokenizer = onyx_tokenizer_create(context.token_alloc, file_contents);
+ onyx_lex_tokens(&tokenizer);
+
+ file_contents->line_count = tokenizer.line_number;
+
+ OnyxParser parser = onyx_parser_create(context.ast_alloc, &tokenizer);
+ onyx_parse(&parser);
+ onyx_parser_free(&parser);
+}
+
+static b32 process_source_file(char* filename, OnyxFilePos error_pos) {
+ bh_arr_each(bh_file_contents, fc, context.loaded_files) {
+ // Duplicates are detected here and since these filenames will be the full path,
+ // string comparing them should be all that is necessary.
+ if (!strcmp(fc->filename, filename)) return 1;
+ }
+
+ bh_file file;
+ bh_file_error err = bh_file_open(&file, filename);
+ if (err != BH_FILE_ERROR_NONE) {
+ if (context.cycle_detected) {
+ onyx_report_error(error_pos, Error_Critical, "Failed to open file %s", filename);
+ }
+ return 0;
+ }
+
+ bh_file_contents fc = bh_file_read_contents(context.token_alloc, &file);
+ bh_file_close(&file);
+
+ bh_arr_push(context.loaded_files, fc);
+
+ if (context.options->verbose_output == 2)
+ bh_printf("Processing source file: %s (%d bytes)\n", file.filename, fc.length);
+
+ parse_source_file(&bh_arr_last(context.loaded_files));
+ return 1;
+}
+
+static b32 process_load_entity(Entity* ent) {
+ assert(ent->type == Entity_Type_Load_File || ent->type == Entity_Type_Load_Path);
+ AstInclude* include = ent->include;
+
+ if (include->kind == Ast_Kind_Load_File) {
+ // :RelativeFiles
+ const char* parent_file = include->token->pos.filename;
+ if (parent_file == NULL) parent_file = ".";
+
+ char* parent_folder = bh_path_get_parent(parent_file, global_scratch_allocator);
+
+ char* filename = bh_lookup_file(include->name, parent_folder, ".onyx", 1, context.options->included_folders, 1);
+ char* formatted_name = bh_strdup(global_heap_allocator, filename);
+
+ return process_source_file(formatted_name, include->token->pos);
+
+ } else if (include->kind == Ast_Kind_Load_All) {
+ const char* parent_file = include->token->pos.filename;
+ if (parent_file == NULL) parent_file = ".";
+
+ char* parent_folder = bh_path_get_parent(parent_file, global_scratch_allocator);
+ char folder[512];
+ if (bh_str_starts_with(include->name, "./")) {
+ bh_snprintf(folder, 511, "%s/%s", parent_folder, include->name + 2);
+ } else {
+ bh_snprintf(folder, 511, "%s", include->name);
+ }
+
+ // This does not take into account #load_path'd folders...
+
+ bh_path_convert_separators(folder);
+ bh_dir dir = bh_dir_open(folder);
+ if (dir == NULL) {
+ onyx_report_error(include->token->pos, Error_Critical, "Could not find folder '%s'.", folder);
+ return 0;
+ }
+
+ bh_dirent entry;
+ b32 success = 1;
+ char fullpath[512];
+ while (bh_dir_read(dir, &entry)) {
+ if (entry.type == BH_DIRENT_FILE && bh_str_ends_with(entry.name, ".onyx")) {
+ bh_snprintf(fullpath, 511, "%s/%s", folder, entry.name);
+ bh_path_convert_separators(fullpath);
+ u8* formatted_name = bh_path_get_full_name(fullpath, global_heap_allocator);
+ success = process_source_file(formatted_name, include->token->pos);
+ if (!success) break;
+ }
+ }
+
+ bh_dir_close(dir);
+ return success;
+
+ } else if (include->kind == Ast_Kind_Load_Path) {
+ bh_arr_push(context.options->included_folders, include->name);
+
+ } else if (include->kind == Ast_Kind_Library_Path) {
+ bh_arr_push(context.wasm_module->library_paths, include->name);
+ }
+
+ return 1;
+}
+
+static b32 process_entity(Entity* ent) {
+ static char verbose_output_buffer[512];
+ if (context.options->verbose_output == 3) {
+ if (ent->expr && ent->expr->token)
+ snprintf(verbose_output_buffer, 511,
+ "%20s | %24s (%d, %d) | %s:%i:%i \n",
+ entity_state_strings[ent->state],
+ entity_type_strings[ent->type],
+ (u32) ent->macro_attempts,
+ (u32) ent->micro_attempts,
+ ent->expr->token->pos.filename,
+ ent->expr->token->pos.line,
+ ent->expr->token->pos.column);
+
+ else if (ent->expr)
+ snprintf(verbose_output_buffer, 511,
+ "%20s | %24s (%d, %d) \n",
+ entity_state_strings[ent->state],
+ entity_type_strings[ent->type],
+ (u32) ent->macro_attempts,
+ (u32) ent->micro_attempts);
+ }
+
+ // CLEANUP: There should be a nicer way to track if the builtins have
+ // already been initialized.
+ static b32 builtins_initialized = 0;
+
+ EntityState before_state = ent->state;
+ switch (before_state) {
+ case Entity_State_Error:
+ if (ent->type != Entity_Type_Error) {
+ onyx_report_error(ent->expr->token->pos, Error_Critical, "Error entity unexpected. This is definitely a compiler bug");
+ } else {
+ onyx_report_error(ent->error->token->pos, Error_Critical, "Static error occured: '%b'", ent->error->error_msg->text, ent->error->error_msg->length);
+ }
+ break;
+
+ case Entity_State_Parse_Builtin:
+ process_load_entity(ent);
+ ent->state = Entity_State_Finalized;
+ break;
+
+ case Entity_State_Introduce_Symbols:
+ // Currently, introducing symbols is handled in the symbol resolution
+ // function. Maybe there should be a different place where that happens?
+ symres_entity(ent);
+ break;
+
+ case Entity_State_Parse:
+ if (!builtins_initialized) {
+ builtins_initialized = 1;
+ initialize_builtins(context.ast_alloc);
+ introduce_build_options(context.ast_alloc);
+ }
+
+ // GROSS
+ if (special_global_entities_remaining == 0) {
+ special_global_entities_remaining--;
+ initalize_special_globals();
+ }
+
+ if (process_load_entity(ent)) {
+ // GROSS
+ if (ent == runtime_info_types_entity
+ || ent == runtime_info_proc_tags_entity
+ || ent == runtime_info_foreign_entity) {
+ special_global_entities_remaining--;
+ }
+
+ ent->state = Entity_State_Finalized;
+ } else {
+ ent->macro_attempts++;
+ }
+ break;
+
+ 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;
+ }
+
+ b32 changed = ent->state != before_state;
+ if (context.options->verbose_output == 3) {
+ if (changed) printf("SUCCESS to %20s | %s", entity_state_strings[ent->state], verbose_output_buffer);
+ else printf("YIELD to %20s | %s", entity_state_strings[ent->state], verbose_output_buffer);
+ }
+
+ return changed;
+}
+
+// Just having fun with some visual output - brendanfh 2020/12/14
+#if defined(_BH_LINUX)
+static void output_dummy_progress_bar() {
+ EntityHeap* eh = &context.entities;
+ if (bh_arr_length(eh->entities) == 0) return;
+
+ static const char* state_colors[] = {
+ "\e[91m", "\e[93m", "\e[94m", "\e[93m",
+ "\e[97m", "\e[95m", "\e[96m", "\e[92m", "\e[91m",
+ };
+
+ printf("\e[2;1H");
+
+ for (i32 i = 0; i < Entity_State_Count - 1; i++) {
+ if (i % 2 == 0) printf("\n");
+ printf("%s %25s \xe2\x96\x88 ", state_colors[i], entity_state_strings[i]);
+ }
+
+ printf("\n\n");
+
+ for (i32 i = 0; i < Entity_Type_Count; i++) {
+ if (eh->type_count[i] == 0) printf("\e[90m");
+ else if ((i32) eh->entities[0]->type == i) printf("\e[92m");
+ else printf("\e[97m");
+
+ printf("%25s (%4d) | ", entity_type_strings[i], eh->type_count[i]);
+
+ printf("\e[0K");
+ for (i32 j = 0; j < Entity_State_Count; j++) {
+ if (eh->all_count[j][i] == 0) continue;
+
+ printf("%s", state_colors[j]);
+
+ i32 count = (eh->all_count[j][i] >> 5) + 1;
+ for (i32 c = 0; c < count * 2; c++) printf("\xe2\x96\x88");
+
+ printf("\e[0m");
+ }
+ printf("\n");
+ }
+}
+#endif
+
+static void dump_cycles() {
+ context.cycle_detected = 1;
+ Entity* ent;
+
+ while (1) {
+ ent = entity_heap_top(&context.entities);
+ entity_heap_remove_top(&context.entities);
+ if (ent->state < Entity_State_Code_Gen) process_entity(ent);
+ else break;
+
+ if (bh_arr_length(context.entities.entities) == 0) {
+ break;
+ }
+ }
+}
+
+static i32 onyx_compile() {
+ u64 start_time = bh_time_curr();
+
+ if (context.options->fun_output)
+ printf("\e[2J");
+
+ while (!bh_arr_is_empty(context.entities.entities)) {
+ Entity* ent = entity_heap_top(&context.entities);
+
+#if defined(_BH_LINUX)
+ if (context.options->fun_output) {
+ output_dummy_progress_bar();
+
+ if (ent->expr->token) {
+ OnyxFilePos pos = ent->expr->token->pos;
+ printf("\e[0K%s on %s in %s:%d:%d\n", entity_state_strings[ent->state], entity_type_strings[ent->type], pos.filename, pos.line, pos.column);
+ }
+
+ // Slowing things down for the effect
+ usleep(1000);
+ }
+#endif
+
+ entity_heap_remove_top(&context.entities);
+ b32 changed = process_entity(ent);
+
+ // NOTE: VERY VERY dumb cycle breaking. Basically, remember the first entity that did
+ // not change (i.e. did not make any progress). Then everytime an entity doesn't change,
+ // check if it is the same entity. If it is, it means all other entities that were processed
+ // between the two occurences didn't make any progress either, and there must be a cycle.
+ // - brendanfh 2021/02/06
+ //
+ // Because of the recent changes to the compiler architecture (again), this condition
+ // does not always hold anymore. There can be nodes that get scheduled multiple times
+ // before the "key" node that will unblock the progress. This means a more sophisticated
+ // cycle detection algorithm must be used.
+ //
+ static Entity* watermarked_node = NULL;
+ static u32 highest_watermark = 0;
+ if (!changed) {
+ if (!watermarked_node) {
+ watermarked_node = ent;
+ highest_watermark = bh_max(highest_watermark, ent->macro_attempts);
+ }
+ else if (watermarked_node == ent) {
+ if (ent->macro_attempts > highest_watermark) {
+ entity_heap_insert_existing(&context.entities, ent);
+
+ if (context.cycle_almost_detected) {
+ dump_cycles();
+ } else {
+ context.cycle_almost_detected = 1;
+ }
+ }
+ }
+ else if (watermarked_node->macro_attempts < ent->macro_attempts) {
+ watermarked_node = ent;
+ highest_watermark = bh_max(highest_watermark, ent->macro_attempts);
+ }
+ } else {
+ watermarked_node = NULL;
+ context.cycle_almost_detected = 0;
+ }
+
+ if (onyx_has_errors()) return ONYX_COMPILER_PROGRESS_ERROR;
+
+ if (ent->state != Entity_State_Finalized && ent->state != Entity_State_Failed)
+ entity_heap_insert_existing(&context.entities, ent);
+ }
+
+ u64 duration = bh_time_duration(start_time);
+
+ if (context.options->verbose_output > 0) {
+ // TODO: Replace these with bh_printf when padded formatting is added.
+ printf("\nStatistics:\n");
+ printf(" Time taken: %lf seconds\n", (double) duration / 1000);
+ printf(" Processed %ld lines (%f lines/second).\n", lexer_lines_processed, ((f32) 1000 * lexer_lines_processed) / (duration));
+ printf(" Processed %ld tokens (%f tokens/second).\n", lexer_tokens_processed, ((f32) 1000 * lexer_tokens_processed) / (duration));
+ printf("\n");
+ }
+
+ return ONYX_COMPILER_PROGRESS_SUCCESS;
+}
+
+static void link_wasm_module() {
+ Package *runtime_var_package = package_lookup("runtime.vars");
+ assert(runtime_var_package);
+
+ AstTyped *link_options_node = (AstTyped *) symbol_raw_resolve(runtime_var_package->scope, "link_options");
+ Type *link_options_type = type_build_from_ast(context.ast_alloc, builtin_link_options_type);
+
+ assert(unify_node_and_type(&link_options_node, link_options_type) == TYPE_MATCH_SUCCESS);
+
+ OnyxWasmLinkOptions link_opts;
+ // CLEANUP: Properly handle this case.
+ assert(onyx_wasm_build_link_options_from_node(&link_opts, link_options_node));
+
+ onyx_wasm_module_link(context.wasm_module, &link_opts);
+}
+
+static CompilerProgress onyx_flush_module() {
+ link_wasm_module();
+
+ // NOTE: Output to file
+ bh_file output_file;
+ if (bh_file_create(&output_file, context.options->target_file) != BH_FILE_ERROR_NONE)
+ return ONYX_COMPILER_PROGRESS_FAILED_OUTPUT;
+
+ if (context.options->verbose_output)
+ bh_printf("Outputting to WASM file: %s\n", output_file.filename);
+
+ // APPARENTLY... the WebAssembly Threading proposal says that the data segment initializations
+ // in a WASM module are copied into the linear memory EVERY time the module is instantiated, not
+ // just the first time. This means that if we are happily chugging along and modifying global state
+ // and then we spawn a thread, that thread will completely wipe all changes to the global and return
+ // it to its original state. This is horrible obviously, but the only thing that is more horrible is
+ // that the best way around this is to create a second WASM module that simply initializes the given
+ // data section. Then have a section module that is actually your code. For right now, this is going
+ // to be fine since the browser is really the only place that multi-threading can be used to any
+ // degree of competency. But still... This is god awful and I hope that there is some other way to
+ // around this down the line.
+ if (context.options->use_multi_threading && !context.options->use_post_mvp_features) {
+ bh_file data_file;
+ if (bh_file_create(&data_file, bh_aprintf(global_scratch_allocator, "%s.data", context.options->target_file)) != BH_FILE_ERROR_NONE)
+ return ONYX_COMPILER_PROGRESS_FAILED_OUTPUT;
+
+ OnyxWasmModule* data_module = bh_alloc_item(global_heap_allocator, OnyxWasmModule);
+ *data_module = onyx_wasm_module_create(global_heap_allocator);
+
+ data_module->data = context.wasm_module->data;
+ context.wasm_module->data = NULL;
+
+ onyx_wasm_module_write_to_file(data_module, data_file);
+ onyx_wasm_module_write_to_file(context.wasm_module, output_file);
+
+ bh_file_close(&data_file);
+ } else {
+ onyx_wasm_module_write_to_file(context.wasm_module, output_file);
+ }
+
+ bh_file_close(&output_file);
+
+ if (context.options->documentation_file != NULL) {
+ OnyxDocumentation docs = onyx_docs_generate();
+ docs.format = Doc_Format_Human;
+ onyx_docs_emit(&docs, context.options->documentation_file);
+ }
+
+ return ONYX_COMPILER_PROGRESS_SUCCESS;
+}
+
+#ifdef ENABLE_RUN_WITH_WASMER
+static b32 onyx_run() {
+ link_wasm_module();
+
+ bh_buffer code_buffer;
+ onyx_wasm_module_write_to_buffer(context.wasm_module, &code_buffer);
+
+ onyx_run_initialize(context.options->debug_enabled);
+
+ if (context.options->verbose_output > 0)
+ bh_printf("Running program:\n");
+
+ return onyx_run_wasm(code_buffer, context.options->passthrough_argument_count, context.options->passthrough_argument_data);
+}
+#endif
+
+int main(int argc, char *argv[]) {
+
+ bh_scratch_init(&global_scratch, bh_heap_allocator(), 256 * 1024); // NOTE: 256 KiB
+ global_scratch_allocator = bh_scratch_allocator(&global_scratch);
+
+ // SPEED: This used to be a managed heap allocator where all allocations
+ // were tracked and would be automatically freed at the end of execution.
+ // I don't know why I ever did that because that is the job of the operating
+ // system when a process exits.
+ global_heap_allocator = bh_heap_allocator();
+
+ CompileOptions compile_opts = compile_opts_parse(global_heap_allocator, argc, argv);
+ context_init(&compile_opts);
+
+ CompilerProgress compiler_progress = ONYX_COMPILER_PROGRESS_ERROR;
+ switch (compile_opts.action) {
+ case ONYX_COMPILE_ACTION_PRINT_HELP: bh_printf(docstring); return 1;
+
+ case ONYX_COMPILE_ACTION_COMPILE:
+ compiler_progress = onyx_compile();
+ if (compiler_progress == ONYX_COMPILER_PROGRESS_SUCCESS) {
+ onyx_flush_module();
+ }
+ break;
+
+ #ifdef ENABLE_RUN_WITH_WASMER
+ case ONYX_COMPILE_ACTION_RUN:
+ compiler_progress = onyx_compile();
+ if (compiler_progress == ONYX_COMPILER_PROGRESS_SUCCESS) {
+ if (!onyx_run()) {
+ compiler_progress = ONYX_COMPILER_PROGRESS_ERROR;
+ }
+ }
+ break;
+ #endif
+
+ default: break;
+ }
+
+ switch (compiler_progress) {
+ case ONYX_COMPILER_PROGRESS_ERROR:
+ onyx_errors_print();
+ break;
+
+ case ONYX_COMPILER_PROGRESS_FAILED_OUTPUT:
+ bh_printf_err("Failed to open file for writing: '%s'\n", compile_opts.target_file);
+ break;
+
+ case ONYX_COMPILER_PROGRESS_SUCCESS:
+ break;
+ }
+
+ context_free();
+
+ bh_scratch_free(&global_scratch);
+ // bh_managed_heap_free(&global_heap);
+
+ return compiler_progress != ONYX_COMPILER_PROGRESS_SUCCESS;
+}
--- /dev/null
+#define BH_DEFINE
+#define BH_NO_TABLE
+#define STB_DS_IMPLEMENTATION
+#include "bh.h"
+
+#include "wasm_emit.h"
+
+int main(int argc, char *argv[]) {
+ i32 wasm_file_idx = 1;
+ if (argc < 2) {
+ fprintf(stderr, "Expected a WASM file to run.\n");
+ return 1;
+ }
+
+ b32 debug = 0;
+ if (!strcmp(argv[1], "--debug")) {
+ debug = 1;
+ wasm_file_idx = 2;
+ }
+
+ if (debug && argc < 3) {
+ fprintf(stderr, "Expected a WASM file to run.\n");
+ return 1;
+ }
+
+ onyx_run_initialize(debug);
+
+ bh_file wasm_file;
+ bh_file_error err = bh_file_open(&wasm_file, argv[wasm_file_idx]);
+ if (err != BH_FILE_ERROR_NONE) {
+ fprintf(stderr, "Failed to open file %s", argv[wasm_file_idx]);
+ return 1;
+ }
+
+ bh_file_contents wasm_data = bh_file_read_contents(bh_heap_allocator(), &wasm_file);
+ bh_file_close(&wasm_file);
+
+ bh_buffer data;
+ data.data = wasm_data.data;
+ data.length = wasm_data.length;
+ return onyx_run_wasm(data, argc - 1 - debug, argv + 1 + debug);
+}
--- /dev/null
+// The sole job of the parser for Onyx is to submit nodes to the
+// entity heap for further processing. These nodes include things
+// such as procedure definitions, string literals, struct definitions
+// and declarations to be introduced into scopes.
+
+#include "lex.h"
+#include "errors.h"
+#include "parser.h"
+#include "utils.h"
+
+#define make_node(nclass, kind) onyx_ast_node_new(parser->allocator, sizeof(nclass), kind)
+// :LinearTokenDependent
+#define peek_token(ahead) (parser->curr + ahead)
+
+static AstNode error_node = { Ast_Kind_Error, 0, NULL, NULL };
+
+#define ENTITY_SUBMIT(node) (submit_entity_in_scope(parser, (AstNode *) (node), parser->current_scope, parser->package))
+#define ENTITY_SUBMIT_IN_SCOPE(node, scope) (submit_entity_in_scope(parser, (AstNode *) (node), scope, parser->package))
+
+void submit_entity_in_scope(OnyxParser* parser, AstNode* node, Scope* scope, Package* package) {
+ if (bh_arr_length(parser->alternate_entity_placement_stack) == 0) {
+ add_entities_for_node(NULL, node, scope, package);
+
+ } else {
+ bh_arr(Entity *) *entity_array = bh_arr_last(parser->alternate_entity_placement_stack);
+ add_entities_for_node(entity_array, node, scope, package);
+ }
+}
+
+// Parsing Utilities
+static void consume_token(OnyxParser* parser);
+static OnyxToken* expect_token(OnyxParser* parser, TokenType token_type);
+static b32 consume_token_if_next(OnyxParser* parser, TokenType token_type);
+static b32 next_tokens_are(OnyxParser* parser, i32 n, ...);
+static OnyxToken* find_matching_paren(OnyxToken* paren);
+
+static AstNumLit* parse_int_literal(OnyxParser* parser);
+static AstNumLit* parse_float_literal(OnyxParser* parser);
+static b32 parse_possible_struct_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret);
+static b32 parse_possible_array_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret);
+static b32 parse_possible_unary_field_access(OnyxParser* parser, AstTyped** ret);
+static void parse_arguments(OnyxParser* parser, TokenType end_token, Arguments* args);
+static AstTyped* parse_factor(OnyxParser* parser);
+static AstTyped* parse_compound_assignment(OnyxParser* parser, AstTyped* lhs);
+static AstTyped* parse_compound_expression(OnyxParser* parser, b32 assignment_allowed);
+static AstTyped* parse_expression(OnyxParser* parser, b32 assignment_allowed);
+static AstIfWhile* parse_if_stmt(OnyxParser* parser);
+static AstIfWhile* parse_while_stmt(OnyxParser* parser);
+static AstFor* parse_for_stmt(OnyxParser* parser);
+static AstSwitchCase* parse_case_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, char* block_name);
+static AstNode* parse_statement(OnyxParser* parser);
+static void parse_polymorphic_variable(OnyxParser* parser, AstType*** next_insertion);
+static AstType* parse_type(OnyxParser* parser);
+static AstTypeOf* parse_typeof(OnyxParser* parser);
+static AstStructType* parse_struct(OnyxParser* parser);
+static AstInterface* parse_interface(OnyxParser* parser);
+static AstConstraint* parse_constraint(OnyxParser* parser);
+static void parse_constraints(OnyxParser* parser, ConstraintContext *constraints);
+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_no_consume(OnyxParser* parser);
+static b32 parse_possible_function_definition(OnyxParser* parser, AstTyped** ret);
+static b32 parse_possible_quick_function_definition_no_consume(OnyxParser* parser);
+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 AstMemRes* parse_memory_reservation(OnyxParser* parser, OnyxToken* symbol, b32 thread_local);
+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, Error_Critical, "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;
+}
+
+
+static void expect_no_stored_tags_pos(OnyxParser *parser, OnyxFilePos pos) {
+ if (bh_arr_length(parser->stored_tags) > 0) {
+ onyx_report_error(pos, Error_Critical, "#tag is not allowed on this element.");
+ parser->hit_unexpected_token = 1;
+ }
+}
+
+static void expect_no_stored_tags(OnyxParser *parser) {
+ expect_no_stored_tags_pos(parser, parser->curr->pos);
+}
+
+static void flush_stored_tags(OnyxParser *parser, bh_arr(AstTyped *) *out_arr) {
+ //
+ // When tag_depth > 0, no tags will be added to the element.
+ // This happens if you have a something like so,
+ //
+ // #tag "asdf"
+ // #tag handler.{ (x: i32) => x * 2 }
+ // foo :: () { ... }
+ //
+ // In this situation, the inner procedure defined in the second
+ // tag should NOT consume the "asdf" tag.
+ //
+ if (bh_arr_length(parser->stored_tags) == 0 || parser->tag_depth > 0) return;
+
+ bh_arr(AstTyped *) arr = *out_arr;
+
+ if (arr == NULL) {
+ bh_arr_new(parser->allocator, arr, bh_arr_length(parser->stored_tags));
+ }
+
+ bh_arr_each(AstTyped *, pexpr, parser->stored_tags) {
+ bh_arr_push(arr, *pexpr);
+ }
+
+ bh_arr_clear(parser->stored_tags);
+
+ *out_arr = arr;
+}
+
+
+// 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;
+
+ int_node->type_node = (AstType *) &basic_type_int_unsized;
+
+ // NOTE: Hex literals are unsigned.
+ if (int_node->token->length >= 2 && int_node->token->text[1] == 'x') {
+ int_node->was_hex_literal = 1;
+ }
+
+ 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;
+
+ //
+ // This has a weird condition to avoid the problem of using a quick function as an argument:
+ // f(x => x + 1)
+ // This shouldn't be a named argument, but this should:
+ // f(g = x => x + 1)
+ //
+ if (next_tokens_are(parser, 2, Token_Type_Symbol, '=') && peek_token(2)->type != '>') {
+ 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)) {
+ ENTITY_SUBMIT(retval);
+ break;
+ }
+ if (parse_possible_quick_function_definition(parser, &retval)) {
+ ENTITY_SUBMIT(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: {
+ if (parse_possible_quick_function_definition(parser, &retval)) {
+ ENTITY_SUBMIT(retval);
+ break;
+ }
+
+ 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->data_id = 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, Error_Critical, "Expected '{' after 'do', got '%s'.", token_name(parser->curr->type));
+ retval = NULL;
+ break;
+ }
+
+ do_block->block = parse_block(parser, 1, NULL);
+
+ retval = (AstTyped *) do_block;
+ break;
+ }
+
+ case '[': {
+ AstType *type = parse_type(parser);
+ retval = (AstTyped *) type;
+ break;
+ }
+
+ case '$': {
+ AstType **tmp = (AstType **) &retval;
+ parse_polymorphic_variable(parser, &tmp);
+ 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_expr = parse_expression(parser, 0);
+ fc->type = type_make_slice(parser->allocator, &basic_types[Basic_Kind_U8]);
+
+ if (parser->current_function_stack && bh_arr_length(parser->current_function_stack) > 0) {
+ bh_arr_push(bh_arr_last(parser->current_function_stack)->nodes_that_need_entities_after_clone, (AstNode *) fc);
+
+ } else {
+ 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->data_id = 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, Error_Critical, "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 = (AstFunction *) 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 = (AstType *) parse_expression(parser, 0);
+
+ 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, "quote")) {
+ 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, NULL);
+ ((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 (next_tokens_are(parser, 2, '#', '(')) {
+ OnyxToken* code_token = expect_token(parser, '#');
+ expect_token(parser, '(');
+
+ 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;
+
+ code_block->code = (AstNode *) parse_expression(parser, 0);
+
+ expect_token(parser, ')');
+
+ retval = (AstTyped *) code_block;
+ break;
+ }
+ else if (parse_possible_directive(parser, "unquote")) {
+ 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;
+ }
+ else if (parse_possible_directive(parser, "cstr")) {
+ // Copy pasted from above.
+ AstStrLit* str_node = make_node(AstStrLit, Ast_Kind_StrLit);
+ str_node->token = expect_token(parser, Token_Type_Literal_String);
+ str_node->data_id = 0;
+ str_node->flags |= Ast_Flag_Comptime;
+ str_node->is_cstr = 1;
+
+ ENTITY_SUBMIT(str_node);
+
+ retval = (AstTyped *) str_node;
+ break;
+ }
+
+ onyx_report_error(parser->curr->pos, Error_Critical, "Invalid directive in expression.");
+ return NULL;
+ }
+
+ default:
+ no_match:
+ onyx_report_error(parser->curr->pos, Error_Critical, "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 '(': {
+ if (!parser->parse_calls) goto factor_parsed;
+
+ 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_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;
+ 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;
+
+ case Binary_Op_Method_Call: return 10;
+
+ 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;
+ case Token_Type_Right_Arrow: return Binary_Op_Method_Call;
+ 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_Method_Call) bin_op = make_node(AstBinaryOp, Ast_Kind_Method_Call);
+ 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, NULL);
+
+ 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, NULL);
+
+ 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, NULL);
+ 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;
+
+ if (parse_possible_directive(parser, "bottom_test")) {
+ while_node->bottom_test = 1;
+ }
+
+ 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, NULL);
+
+ if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
+ while_node->false_stmt = parse_block(parser, 1, NULL);
+ }
+
+ 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 (parse_possible_directive(parser, "no_close")) {
+ for_node->no_close = 1;
+ }
+
+ if (consume_token_if_next(parser, '^')) {
+ for_node->by_pointer = 1;
+ }
+
+ if (next_tokens_are(parser, 2, Token_Type_Symbol, ':')) {
+ 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, ':');
+ } else {
+ // HACK
+ static char it_name[] = "it ";
+ static OnyxToken it_token = { Token_Type_Symbol, 2, it_name, { 0 } };
+
+ AstLocal* var_node = make_local(parser->allocator, &it_token, NULL);
+ for_node->var = var_node;
+ }
+
+ for_node->iter = parse_expression(parser, 1);
+ for_node->stmt = parse_block(parser, 1, NULL);
+
+ return for_node;
+}
+
+static AstSwitchCase* parse_case_stmt(OnyxParser* parser) {
+ AstSwitchCase *sc_node = make_node(AstSwitchCase, Ast_Kind_Switch_Case);
+ sc_node->token = expect_token(parser, Token_Type_Keyword_Case);
+
+ if (parse_possible_directive(parser, "default")) {
+ sc_node->is_default = 1;
+
+ } else {
+ bh_arr_new(global_heap_allocator, sc_node->values, 1);
+
+ AstTyped* value = parse_expression(parser, 1);
+ bh_arr_push(sc_node->values, value);
+ while (consume_token_if_next(parser, ',')) {
+ if (parser->hit_unexpected_token) return sc_node;
+
+ value = parse_expression(parser, 1);
+ bh_arr_push(sc_node->values, value);
+ }
+ }
+
+ sc_node->block = parse_block(parser, 1, NULL);
+
+ return sc_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);
+
+ 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;
+
+ switch_node->case_block = parse_block(parser, 1, NULL);
+ 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 == '\'') 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);
+ AstNode* sym_node = make_symbol(parser->allocator, local_sym);
+ bh_arr_push(local_compound->exprs, (AstTyped *) sym_node);
+
+ if (!consume_token_if_next(parser, '\'')) {
+ 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;
+ }
+
+ 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 == ',' ||
+ (peek_token(1)->type == '\'' && peek_token(2)->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 == ':') {
+ bh_arr_push(parser->current_symbol_stack, symbol);
+ AstBinding* binding = parse_top_level_binding(parser, symbol);
+ bh_arr_pop(parser->current_symbol_stack);
+ 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.as_name = expect_token(parser, Token_Type_Symbol);
+ qu.symbol_name = qu.as_name;
+
+ if (consume_token_if_next(parser, ':')) {
+ expect_token(parser, ':');
+ qu.symbol_name = expect_token(parser, Token_Type_Symbol);
+ }
+
+ 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, NULL);
+ 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);
+ if (retval->kind == Ast_Kind_Call || retval->kind == Ast_Kind_Method_Call) {
+ if (parser->curr->type == '{') {
+ AstCodeBlock* code_block = make_node(AstCodeBlock, Ast_Kind_Code_Block);
+ code_block->token = parser->curr;
+ code_block->type_node = builtin_code_type;
+
+ code_block->code = (AstNode *) parse_block(parser, 1, NULL);
+ ((AstBlock *) code_block->code)->rules = Block_Rule_Code_Block;
+
+ AstCall *dest = (AstCall *) retval;
+ if (dest->kind == Ast_Kind_Method_Call) {
+ dest = (AstCall *) ((AstBinaryOp *) dest)->right;
+ if (dest->kind != Ast_Kind_Call) {
+ onyx_report_error(retval->token->pos, Error_Critical, "Expected function call on right side of '->'.");
+ needs_semicolon = 0;
+ break;
+ }
+ }
+
+ bh_arr_push(dest->args.values, (AstTyped *) make_argument(context.ast_alloc, (AstTyped *) code_block));
+ needs_semicolon = 0;
+ }
+ }
+ 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_Case:
+ needs_semicolon = 0;
+ retval = (AstNode *) parse_case_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")) {
+ // :LinearTokenDependent
+ OnyxToken* directive_token = parser->curr - 2;
+
+ OnyxToken* sym_token = bh_alloc_item(parser->allocator, OnyxToken);
+ sym_token->type = Token_Type_Symbol;
+ sym_token->length = 15;
+ sym_token->text = bh_strdup(parser->allocator, "__saved_context ");
+ sym_token->pos = ((OnyxFilePos) {0});
+
+ AstNode *sym_node = make_symbol(parser->allocator, sym_token);
+
+ AstLocal* context_tmp = make_local(parser->allocator, sym_token, NULL);
+ retval = (AstNode *) context_tmp;
+
+ AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
+ assignment->token = directive_token;
+ assignment->operation = Binary_Op_Assign;
+ assignment->left = (AstTyped *) sym_node;
+ assignment->right = builtin_context_variable;
+ context_tmp->next = (AstNode *) assignment;
+
+ AstBinaryOp* assignment2 = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
+ assignment2->token = directive_token + 1;
+ assignment2->operation = Binary_Op_Assign;
+ assignment2->left = builtin_context_variable;
+ assignment2->right = (AstTyped *) sym_node;
+
+ AstDefer* defer_node = make_node(AstDefer, Ast_Kind_Defer);
+ defer_node->token = directive_token;
+ defer_node->stmt = (AstNode *) assignment2;
+ assignment->next = (AstNode *) defer_node;
+
+ AstBlock* context_block = parse_block(parser, 1, NULL);
+ defer_node->next = context_block->body;
+ context_block->body = (AstNode *) context_tmp;
+
+ needs_semicolon = 0;
+ break;
+ }
+
+ if (next_tokens_are(parser, 2, '#', Token_Type_Keyword_If)) {
+ AstIf* static_if = parse_static_if_stmt(parser, 1);
+
+ assert(parser->current_function_stack && bh_arr_length(parser->current_function_stack) > 0);
+ bh_arr_push(bh_arr_last(parser->current_function_stack)->nodes_that_need_entities_after_clone, (AstNode *) static_if);
+
+ needs_semicolon = 0;
+ retval = (AstNode *) static_if;
+ break;
+ }
+
+ if (parse_possible_directive(parser, "persist")) {
+ b32 thread_local = parse_possible_directive(parser, "thread_local");
+
+ OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
+ AstMemRes* memres = parse_memory_reservation(parser, symbol, thread_local);
+
+ AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
+ binding->token = memres->token;
+ binding->node = (AstNode *) memres;
+ ENTITY_SUBMIT(binding);
+ break;
+ }
+
+ if (parse_possible_directive(parser, "remove")) {
+ // :LinearTokenDependent
+ AstDirectiveRemove *remove = make_node(AstDirectiveRemove, Ast_Kind_Directive_Remove);
+ remove->token = parser->curr - 2;
+ retval = (AstNode *) remove;
+ 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, char* block_name) {
+ 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);
+ block->binding_scope->name = block_name;
+ 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, Error_Critical, "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 '$': {
+ 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 == '(' && parser->parse_calls) {
+ 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_expression(parser, 0);
+ 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;
+ }
+
+ //
+ // I don't think any of these cases are necesary any more?
+ 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:
+ case '-': {
+ *next_insertion = (AstType *) parse_expression(parser, 0);
+ 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, Error_Critical, "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 void struct_type_create_scope(OnyxParser *parser, AstStructType *s_node) {
+ 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* current_symbol = bh_arr_last(parser->current_symbol_stack);
+ s_node->scope->name = bh_aprintf(global_heap_allocator, "%b", current_symbol->text, current_symbol->length);
+ }
+}
+
+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;
+
+ flush_stored_tags(parser, &s_node->meta_tags);
+
+ // Parse polymorphic parameters
+ 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;
+ }
+
+ // Parse constraints clause
+ if (parser->curr->type == Token_Type_Keyword_Where) {
+ parse_constraints(parser, &s_node->constraints);
+ }
+
+ bh_arr_new(global_heap_allocator, s_node->members, 4);
+
+ // Parse directives
+ while (parser->curr->type == '#') {
+ if (parser->hit_unexpected_token) return NULL;
+
+ if (parse_possible_directive(parser, "union")) s_node->is_union = 1;
+
+ else if (parse_possible_directive(parser, "pack")) s_node->is_packed = 1;
+
+ else if (parse_possible_directive(parser, "align")) {
+ s_node->min_alignment_ = parse_expression(parser, 0);
+ }
+
+ else if (parse_possible_directive(parser, "size")) {
+ s_node->min_size_ = parse_expression(parser, 0);
+ }
+
+ else {
+ OnyxToken* directive_token = expect_token(parser, '#');
+ OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
+
+ onyx_report_error(directive_token->pos, Error_Critical, "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;
+
+ if (parse_possible_directive(parser, "persist")) {
+ struct_type_create_scope(parser, s_node);
+
+ b32 thread_local = parse_possible_directive(parser, "thread_local");
+
+ OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
+ AstMemRes* memres = parse_memory_reservation(parser, symbol, thread_local);
+ consume_token_if_next(parser, ';');
+
+ AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
+ binding->token = memres->token;
+ binding->node = (AstNode *) memres;
+ ENTITY_SUBMIT(binding);
+ continue;
+ }
+
+ if (next_tokens_are(parser, 3, Token_Type_Symbol, ':', ':')) {
+ struct_type_create_scope(parser, s_node);
+
+ 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, ';');
+ continue;
+ }
+
+ bh_arr(AstTyped *) meta_tags=NULL;
+ while (parse_possible_directive(parser, "tag")) {
+ if (meta_tags == NULL) bh_arr_new(global_heap_allocator, meta_tags, 1);
+
+ parser->tag_depth += 1;
+
+ do {
+ AstTyped* expr = parse_expression(parser, 0);
+ bh_arr_push(meta_tags, expr);
+ } while (consume_token_if_next(parser, ','));
+
+ parser->tag_depth -= 1;
+ }
+
+ member_is_used = consume_token_if_next(parser, Token_Type_Keyword_Use);
+
+ 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, Error_Critical, "'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, Error_Critical, "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;
+ mem->meta_tags = meta_tags;
+
+ if (member_is_used) mem->is_used = 1;
+
+ bh_arr_push(s_node->members, mem);
+ }
+
+ if (peek_token(0)->type != '}') {
+ 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 {
+ ENTITY_SUBMIT(s_node);
+ return s_node;
+ }
+}
+
+static AstInterface* parse_interface(OnyxParser* parser) {
+ AstInterface *interface = make_node(AstInterface, Ast_Kind_Interface);
+ interface->token = expect_token(parser, Token_Type_Keyword_Interface);
+
+ bh_arr_new(global_heap_allocator, interface->params, 2);
+
+ expect_token(parser, '(');
+ while (!consume_token_if_next(parser, ')')) {
+ if (parser->hit_unexpected_token) return interface;
+
+ InterfaceParam ip;
+ ip.value_token = expect_token(parser, Token_Type_Symbol);
+ expect_token(parser, ':');
+ expect_token(parser, '$');
+ ip.type_token = expect_token(parser, Token_Type_Symbol);
+
+ bh_arr_push(interface->params, ip);
+
+ if (parser->curr->type != ')')
+ expect_token(parser, ',');
+ }
+
+ bh_arr_new(global_heap_allocator, interface->exprs, 2);
+
+ expect_token(parser, '{');
+ while (!consume_token_if_next(parser, '}')) {
+ if (parser->hit_unexpected_token) return interface;
+
+ InterfaceConstraint ic = {0};
+ if (parse_possible_directive(parser, "not")) {
+ ic.invert_condition = 1;
+ }
+
+ if (consume_token_if_next(parser, '{')) {
+ ic.expr = parse_expression(parser, 0);
+
+ expect_token(parser, '}');
+ expect_token(parser, Token_Type_Right_Arrow);
+
+ ic.expected_type_expr = parse_type(parser);
+
+ } else {
+ ic.expr = parse_expression(parser, 0);
+ }
+
+ bh_arr_push(interface->exprs, ic);
+
+ expect_token(parser, ';');
+ }
+
+ return interface;
+}
+
+static AstConstraint* parse_constraint(OnyxParser* parser) {
+ AstConstraint* constraint = make_node(AstConstraint, Ast_Kind_Constraint);
+
+ parser->parse_calls = 0;
+ constraint->interface = (AstInterface *) parse_factor(parser);
+ parser->parse_calls = 1;
+
+ constraint->token = constraint->interface->token;
+
+ bh_arr_new(global_heap_allocator, constraint->type_args, 2);
+
+ expect_token(parser, '(');
+ while (!consume_token_if_next(parser, ')')) {
+ if (parser->hit_unexpected_token) return constraint;
+
+ AstType* type_node = parse_type(parser);
+ bh_arr_push(constraint->type_args, type_node);
+
+ if (parser->curr->type != ')')
+ expect_token(parser, ',');
+ }
+
+ return constraint;
+}
+
+static void parse_constraints(OnyxParser* parser, ConstraintContext *out_constraints) {
+ bh_arr_new(global_heap_allocator, out_constraints->constraints, 2);
+
+ expect_token(parser, Token_Type_Keyword_Where);
+
+ do {
+ AstConstraint *constraint = parse_constraint(parser);
+ if (parser->hit_unexpected_token) return;
+
+ bh_arr_push(out_constraints->constraints, constraint);
+ } while (consume_token_if_next(parser, ','));
+}
+
+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);
+
+ bh_arr(AstParam) param_buffer=NULL;
+ bh_arr_new(global_heap_allocator, param_buffer, 2);
+
+ OnyxToken* symbol;
+ while (!consume_token_if_next(parser, ')')) {
+ do {
+ if (parser->hit_unexpected_token) return;
+
+ b32 param_use = 0;
+ b32 param_is_baked = 0;
+ 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);
+
+ 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.is_used = 1;
+ param_use = 0;
+ }
+
+ if (param_is_baked) {
+ curr_param.is_baked = 1;
+ param_is_baked = 0;
+ }
+
+ bh_arr_push(param_buffer, curr_param);
+ } while (consume_token_if_next(parser, ','));
+
+ expect_token(parser, ':');
+
+ VarArgKind vararg_kind=VA_Kind_Not_VA;
+ AstType* type_node=NULL;
+ AstTyped* default_value=NULL;
+
+ if (parser->curr->type != '=') {
+ if (consume_token_if_next(parser, Token_Type_Dot_Dot)) {
+ if (consume_token_if_next(parser, '.')) vararg_kind = VA_Kind_Untyped;
+ else vararg_kind = VA_Kind_Typed;
+ }
+
+ if (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);
+ type_node = parse_type(parser);
+ i32 new_len = bh_arr_length(*parser->polymorph_context.poly_params);
+
+ if (vararg_kind == VA_Kind_Typed) {
+ AstVarArgType* va_type = make_node(AstVarArgType, Ast_Kind_VarArg_Type);
+ va_type->elem = type_node;
+ va_type->token = type_node->token;
+ type_node = (AstType *) va_type;
+ }
+
+ fori (i, 0, new_len - old_len) {
+ (*parser->polymorph_context.poly_params)[old_len + i].type_expr = type_node;
+ (*parser->polymorph_context.poly_params)[old_len + i].idx = param_idx;
+ }
+ }
+ }
+
+ if (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;
+ default_value = (AstTyped *) cs;
+
+ } else {
+ default_value = parse_expression(parser, 0);
+ }
+ }
+
+ bh_arr_each(AstParam, param, param_buffer) {
+ param->vararg_kind = vararg_kind;
+ param->local->type_node = type_node;
+ param->default_value = default_value;
+
+ if (param->is_baked) {
+ bh_arr(AstPolyParam) pv = *parser->polymorph_context.poly_params;
+ bh_arr_push(pv, ((AstPolyParam) {
+ .kind = PPK_Baked_Value,
+ .idx = param_idx,
+
+ .poly_sym = (AstNode *) param->local,
+ .type_expr = type_node,
+ }));
+
+ *parser->polymorph_context.poly_params = pv;
+ }
+
+ bh_arr_push(func->params, *param);
+ param_idx++;
+ }
+
+ bh_arr_clear(param_buffer);
+
+ if (parser->curr->type != ')')
+ expect_token(parser, ',');
+ }
+
+ bh_arr_free(param_buffer);
+ return;
+}
+
+static AstOverloadedFunction* parse_overloaded_function(OnyxParser* parser, OnyxToken* token) {
+ b32 locked = 0;
+ if (parse_possible_directive(parser, "locked")) {
+ locked = 1;
+ }
+
+ b32 local = 0;
+ if (parse_possible_directive(parser, "local")) {
+ local = 1;
+ }
+
+ // This could be checked elsewhere?
+ if (locked && local) {
+ onyx_report_error(token->pos, Error_Critical, "Only one of '#locked' and '#local' can because use at a time.");
+ }
+
+ expect_token(parser, '{');
+
+ AstOverloadedFunction* ofunc = make_node(AstOverloadedFunction, Ast_Kind_Overloaded_Function);
+ ofunc->token = token;
+ ofunc->flags |= Ast_Flag_Comptime;
+ ofunc->locked = locked;
+ ofunc->only_local_functions = local;
+
+ 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_push(parser->current_function_stack, func_def);
+
+ flush_stored_tags(parser, &func_def->tags);
+
+ 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);
+ // defer bh_arr_free(polymorphic_vars);
+
+ 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;
+
+ char* name = NULL;
+ if (bh_arr_length(parser->current_symbol_stack) > 0) {
+ OnyxToken *current_symbol = bh_arr_last(parser->current_symbol_stack);
+ name = bh_aprintf(global_heap_allocator, "%b", current_symbol->text, current_symbol->length);
+ }
+
+ if (consume_token_if_next(parser, '=')) {
+ expect_token(parser, '>');
+ func_def->return_type = (AstType *) &basic_type_auto_return;
+
+ if (parser->curr->type == '{') {
+ func_def->body = parse_block(parser, 1, name);
+
+ } else {
+ AstTyped* returned_value = parse_compound_expression(parser, 0);
+ if (returned_value == NULL) goto function_defined;
+
+ AstReturn* return_node = make_node(AstReturn, Ast_Kind_Return);
+ return_node->token = returned_value->token;
+ return_node->expr = returned_value;
+
+ AstBlock* body_block = make_node(AstBlock, Ast_Kind_Block);
+ body_block->token = returned_value->token;
+ body_block->body = (AstNode *) return_node;
+
+ func_def->body = body_block;
+ }
+
+ goto function_defined;
+ }
+
+ 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);
+ }
+ }
+
+ if (parser->curr->type == Token_Type_Keyword_Where) {
+ parse_constraints(parser, &func_def->constraints);
+ }
+
+ while (parser->curr->type == '#') {
+ if (parse_possible_directive(parser, "intrinsic")) {
+ func_def->is_intrinsic = 1;
+
+ 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->is_foreign = 1;
+ }
+
+ // 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, Error_Critical, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
+ }
+ }
+
+ func_def->body = parse_block(parser, 1, name);
+
+ // :LinearTokenDependent
+ func_def->closing_brace = parser->curr - 1;
+
+function_defined:
+ if (bh_arr_length(polymorphic_vars) > 0) {
+ func_def->kind = Ast_Kind_Polymorphic_Proc;
+ func_def->poly_params = polymorphic_vars;
+
+ } else {
+ bh_arr_free(polymorphic_vars);
+ }
+
+ bh_arr_pop(parser->current_function_stack);
+ return func_def;
+}
+
+static b32 parse_possible_function_definition_no_consume(OnyxParser* parser) {
+ if (parser->curr->type == '(') {
+ OnyxToken* matching_paren = find_matching_paren(parser->curr);
+ if (matching_paren == NULL) return 0;
+
+ if (next_tokens_are(parser, 4, '(', ')', '=', '>')) 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
+ && token_after_paren->type != Token_Type_Keyword_Where
+ && (token_after_paren->type != '=' || (token_after_paren + 1)->type != '>'))
+ 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;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static b32 parse_possible_function_definition(OnyxParser* parser, AstTyped** ret) {
+ if (parse_possible_function_definition_no_consume(parser)) {
+ OnyxToken* proc_token = parser->curr;
+ AstFunction* func_node = parse_function_definition(parser, proc_token);
+ *ret = (AstTyped *) func_node;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+typedef struct QuickParam {
+ OnyxToken* token;
+ b32 is_baked;
+} QuickParam;
+
+static b32 parse_possible_quick_function_definition_no_consume(OnyxParser* parser) {
+ //
+ // x => x + 1 case.
+ if (next_tokens_are(parser, 3, Token_Type_Symbol, '=', '>')) {
+ return 1;
+ }
+
+ 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;
+
+ return 1;
+}
+
+static b32 parse_possible_quick_function_definition(OnyxParser* parser, AstTyped** ret) {
+ if (!parse_possible_quick_function_definition_no_consume(parser)) return 0;
+
+ bh_arr(QuickParam) params=NULL;
+ bh_arr_new(global_heap_allocator, params, 4);
+ OnyxToken* proc_token;
+
+ if (parser->curr->type == Token_Type_Symbol) {
+ QuickParam param = { 0 };
+ param.token = expect_token(parser, Token_Type_Symbol);
+ proc_token = param.token;
+ bh_arr_push(params, param);
+
+ } else {
+ proc_token = expect_token(parser, '(');
+ while (parser->curr->type != ')') {
+ if (parser->hit_unexpected_token) return 0;
+
+ QuickParam param = { 0 };
+ if (consume_token_if_next(parser, '$')) param.is_baked = 1;
+ param.token = expect_token(parser, Token_Type_Symbol);
+
+ bh_arr_push(params, param);
+
+ 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(QuickParam, param, params) {
+ char text[512];
+ memset(text, 0, 512);
+ strncat(text, "__type_", 511);
+ token_toggle_end(param->token);
+ strncat(text, param->token->text, 511);
+ token_toggle_end(param->token);
+
+ OnyxToken* new_token = bh_alloc(parser->allocator, sizeof(OnyxToken));
+ new_token->type = Token_Type_Symbol;
+ new_token->length = 7 + param->token->length;
+ new_token->text = bh_strdup(parser->allocator, text);
+ new_token->pos = param->token->pos;
+
+ AstNode* type_node = make_symbol(parser->allocator, new_token);
+ bh_arr_push(poly_params, type_node);
+ }
+
+ AstFunction* poly_proc = make_node(AstFunction, Ast_Kind_Polymorphic_Proc);
+
+ bh_arr_new(global_heap_allocator, poly_proc->params, bh_arr_length(params));
+ fori (i, 0, bh_arr_length(params)) {
+ AstLocal* param_local = make_local(parser->allocator, params[i].token, (AstType *) poly_params[i]);
+ param_local->kind = Ast_Kind_Param;
+
+ bh_arr_push(poly_proc->params, ((AstParam) {
+ .local = param_local,
+ .default_value = NULL,
+
+ .vararg_kind = 0,
+ .use_processed = 0,
+ }));
+ }
+
+ AstBlock* body_block;
+ AstType* return_type;
+ bh_arr_push(parser->current_function_stack, poly_proc);
+
+ if (parser->curr->type == '{') {
+ char* name = NULL;
+ if (bh_arr_length(parser->current_symbol_stack) > 0) {
+ OnyxToken *current_symbol = bh_arr_last(parser->current_symbol_stack);
+ name = bh_aprintf(global_heap_allocator, "%b", current_symbol->text, current_symbol->length);
+ }
+
+ body_block = parse_block(parser, 1, name);
+ 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;
+ }
+
+ poly_proc->token = proc_token;
+ poly_proc->body = body_block;
+ poly_proc->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,
+ }));
+
+ if (params[i].is_baked) {
+ // This is not handled currently, as you cannot say f :: ($x: $T) yet, which is what this would have to do.
+ }
+ }
+
+ *ret = (AstTyped *) poly_proc;
+
+ bh_arr_pop(parser->current_function_stack);
+ bh_arr_free(params);
+ bh_arr_free(poly_params);
+ return 1;
+}
+
+static AstTyped* parse_global_declaration(OnyxParser* parser) {
+ expect_no_stored_tags(parser);
+
+ AstGlobal* global_node = make_node(AstGlobal, Ast_Kind_Global);
+ global_node->token = expect_token(parser, Token_Type_Keyword_Global);
+
+ global_node->type_node = parse_type(parser);
+
+ ENTITY_SUBMIT(global_node);
+
+ return (AstTyped *) global_node;
+}
+
+static AstEnumType* parse_enum_declaration(OnyxParser* parser) {
+ expect_no_stored_tags(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 (parser->hit_unexpected_token) return enum_node;
+
+ if (parse_possible_directive(parser, "flags")) {
+ enum_node->is_flags = 1;
+ } else {
+ OnyxToken* directive_token = expect_token(parser, '#');
+ OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
+
+ onyx_report_error(directive_token->pos, Error_Critical, "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, ':');
+ evalue->value = parse_expression(parser, 0);
+ }
+
+ 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) {
+ expect_no_stored_tags(parser);
+
+ AstIf* static_if_node = make_node(AstIf, Ast_Kind_Static_If);
+ static_if_node->token = expect_token(parser, '#');
+ static_if_node->defined_in_scope = parser->current_scope;
+ 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, NULL);
+
+ } 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, NULL);
+
+ } 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 AstMemRes* parse_memory_reservation(OnyxParser* parser, OnyxToken* symbol, b32 threadlocal) {
+ expect_token(parser, ':');
+
+ expect_no_stored_tags(parser);
+
+ AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres);
+ memres->threadlocal = threadlocal;
+ 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);
+ return memres;
+}
+
+static AstMacro* parse_macro(OnyxParser* parser) {
+ expect_no_stored_tags(parser);
+
+ AstMacro* macro = make_node(AstMacro, Ast_Kind_Macro);
+ macro->token = expect_token(parser, Token_Type_Keyword_Macro);
+
+ if (parse_possible_function_definition(parser, ¯o->body)) {
+ ENTITY_SUBMIT(macro);
+ return macro;
+ }
+
+ if (parse_possible_quick_function_definition(parser, ¯o->body)) {
+ ENTITY_SUBMIT(macro);
+ return macro;
+ }
+
+ onyx_report_error(parser->curr->pos, Error_Critical, "'macro' expects to be followed by a producure definition.");
+ return NULL;
+}
+
+static AstDirectiveInit* parse_init_directive(OnyxParser *parser, OnyxToken *token) {
+ AstDirectiveInit *init = make_node(AstDirectiveInit, Ast_Kind_Directive_Init);
+ init->token = token;
+
+ parser->parse_calls = 0;
+ while (parse_possible_directive(parser, "after")) {
+ if (parser->hit_unexpected_token) return init;
+ if (init->dependencies == NULL) bh_arr_new(global_heap_allocator, init->dependencies, 2);
+
+ AstTyped *dependency = parse_expression(parser, 0);
+ bh_arr_push(init->dependencies, (AstDirectiveInit *) dependency);
+ }
+ parser->parse_calls = 1;
+
+ init->init_proc = parse_expression(parser, 0);
+ ENTITY_SUBMIT(init);
+ return init;
+}
+
+static AstForeignBlock* parse_foreign_block(OnyxParser* parser, OnyxToken *token) {
+ // :LinearTokenDependent
+ AstForeignBlock *fb = make_node(AstForeignBlock, Ast_Kind_Foreign_Block);
+ fb->token = token;
+ fb->module_name = expect_token(parser, Token_Type_Literal_String);
+
+ //
+ // This has a fun implication that there cannot be foreign blocks in the builtin
+ // or type_info packages, as those are loaded before foreign_block_type has a value.
+ fb->type_node = foreign_block_type;
+
+ bh_arr_new(global_heap_allocator, fb->captured_entities, 4);
+ bh_arr_push(parser->alternate_entity_placement_stack, &fb->captured_entities);
+
+ expect_token(parser, '{');
+ parse_top_level_statements_until(parser, '}');
+ expect_token(parser, '}');
+
+ bh_arr_pop(parser->alternate_entity_placement_stack);
+ ENTITY_SUBMIT(fb);
+
+ return fb;
+}
+
+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_Interface) return (AstTyped *) parse_interface(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 (parser->curr->type == '#') {
+ 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;
+ }
+
+ if (parse_possible_directive(parser, "init")) {
+ // :LinearTokenDependent
+ AstDirectiveInit *init = parse_init_directive(parser, parser->curr - 2);
+ return (AstTyped *) init;
+ }
+
+ if (parse_possible_directive(parser, "distinct")) {
+ // :LinearTokenDependent
+ AstDistinctType *distinct = make_node(AstDistinctType, Ast_Kind_Distinct_Type);
+ distinct->token = parser->curr - 2;
+ distinct->base_type = parse_type(parser);
+ return (AstTyped *) distinct;
+ }
+
+ if (parse_possible_directive(parser, "foreign")) {
+ AstForeignBlock *foreign = parse_foreign_block(parser, parser->curr - 2);
+ return (AstTyped *) foreign;
+ }
+ }
+
+ return parse_expression(parser, 1);
+}
+
+static char* generate_name_within_scope(OnyxParser* parser, OnyxToken* symbol) {
+ char name[512];
+ memset(name, 0, 512);
+
+ bh_arr(char *) names=NULL;
+ bh_arr_new(global_heap_allocator, names, 4);
+
+ Scope* scope = parser->current_scope;
+ while (scope != NULL) {
+ bh_arr_push(names, scope->name);
+ scope = scope->parent;
+ }
+
+ bh_arr_each(char *, n, names) {
+ if (*n == NULL) continue;
+
+ strncat(name, *n, 511);
+ strncat(name, ".", 511);
+ }
+ bh_arr_free(names);
+
+ return bh_aprintf(global_heap_allocator, "%s%b", name, symbol->text, symbol->length);
+}
+
+static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol) {
+ OnyxToken *after_second_colon = expect_token(parser, ':');
+ if (after_second_colon) after_second_colon += 1;
+
+ AstTyped* node = parse_top_level_expression(parser);
+ if (parser->hit_unexpected_token || node == NULL)
+ return NULL;
+
+ switch (node->kind) {
+ case Ast_Kind_Function:
+ case Ast_Kind_Polymorphic_Proc: {
+ AstFunction* func = (AstFunction *) node;
+
+ if (func->intrinsic_name == NULL) func->intrinsic_name = symbol;
+
+ func->name = generate_name_within_scope(parser, symbol);
+ break;
+ }
+
+ case Ast_Kind_Macro: {
+ AstMacro* macro = (AstMacro *) node;
+
+ AstFunction* func = (AstFunction *) macro->body;
+ func->name = generate_name_within_scope(parser, symbol);
+ break;
+ }
+
+ case Ast_Kind_Directive_Init: break;
+
+ case Ast_Kind_Global: ((AstGlobal *) node)->name = generate_name_within_scope(parser, symbol);
+
+ case Ast_Kind_Overloaded_Function:
+ case Ast_Kind_StrLit:
+ break;
+
+ case Ast_Kind_Interface:
+ case Ast_Kind_Struct_Type:
+ case Ast_Kind_Poly_Struct_Type:
+ case Ast_Kind_Enum_Type:
+ case Ast_Kind_Distinct_Type:
+ ((AstStructType *) node)->name = generate_name_within_scope(parser, symbol);
+ goto default_case;
+
+ case Ast_Kind_Type_Alias:
+ node->token = symbol;
+ goto default_case;
+
+ case Ast_Kind_Package: goto default_case;
+ case Ast_Kind_NumLit: goto default_case;
+
+ default: {
+ if (!node_is_type((AstNode *) node)) {
+ AstAlias* alias = make_node(AstAlias, Ast_Kind_Alias);
+ alias->token = node->token;
+ alias->alias = node;
+ node = (AstTyped *) alias;
+ }
+
+default_case:
+ ENTITY_SUBMIT(node);
+ }
+ }
+
+ AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
+ binding->token = symbol;
+ binding->node = (AstNode *) node;
+
+ if (after_second_colon) expect_no_stored_tags_pos(parser, after_second_colon->pos);
+ 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 (next_tokens_are(parser, 2, '#', Token_Type_Keyword_Package)) {
+ consume_tokens(parser, 2);
+ 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, "local")) {
+ 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);
+
+ if (next_tokens_are(parser, 2, ':', ':')) {
+ expect_token(parser, ':');
+
+ bh_arr_push(parser->current_symbol_stack, symbol);
+ binding = parse_top_level_binding(parser, symbol);
+ bh_arr_pop(parser->current_symbol_stack);
+
+ if (binding != NULL) binding->flags |= private_kind;
+
+ goto submit_binding_to_entities;
+ }
+
+ AstMemRes* memres = parse_memory_reservation(parser, symbol, 0);
+
+ binding = make_node(AstBinding, Ast_Kind_Binding);
+ binding->token = symbol;
+ binding->flags |= private_kind;
+ binding->node = (AstNode *) memres;
+
+ goto submit_binding_to_entities;
+ }
+
+ case '(': {
+ AstTyped *retval = NULL;
+ if (parse_possible_function_definition(parser, &retval)) {
+ ENTITY_SUBMIT(retval);
+ return;
+ }
+ if (parse_possible_quick_function_definition(parser, &retval)) {
+ ENTITY_SUBMIT(retval);
+ return;
+ }
+ break;
+ }
+
+ 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;
+ include->name_node = parse_expression(parser, 0);
+
+ ENTITY_SUBMIT(include);
+ return;
+ }
+ else if (parse_possible_directive(parser, "load_all")) {
+ AstInclude* include = make_node(AstInclude, Ast_Kind_Load_All);
+ include->token = dir_token;
+ include->name_node = parse_expression(parser, 0);
+
+ 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;
+ include->name_node = parse_expression(parser, 0);
+
+ ENTITY_SUBMIT(include);
+ return;
+ }
+ else if (parse_possible_directive(parser, "library_path")) {
+ AstInclude* include = make_node(AstInclude, Ast_Kind_Library_Path);
+ include->token = dir_token;
+ include->name_node = parse_expression(parser, 0);
+
+ 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, "foreign")) {
+ parse_foreign_block(parser, parser->curr - 2);
+ return;
+ }
+ else if (parse_possible_directive(parser, "operator")) {
+ AstDirectiveOperator *operator = make_node(AstDirectiveOperator, Ast_Kind_Directive_Operator);
+ operator->token = dir_token;
+
+ // These cases have to happen first because these are not necessarily "binary operators",
+ // they are just things that I want to be able to overload. []= is technically a ternary
+ // operator so all these things are horribly named anyway.
+ if (next_tokens_are(parser, 3, '^', '[', ']')) {
+ consume_tokens(parser, 3);
+ operator->operator = Binary_Op_Ptr_Subscript;
+ goto operator_determined;
+ }
+
+ if (next_tokens_are(parser, 3, '[', ']', '=')) {
+ consume_tokens(parser, 3);
+ operator->operator = Binary_Op_Subscript_Equals;
+ goto operator_determined;
+ }
+
+ // The default case
+ 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, Error_Critical, "Invalid binary operator.");
+ } else {
+ operator->operator = op;
+ }
+
+ operator_determined:
+ operator->overload = parse_expression(parser, 0);
+
+ ENTITY_SUBMIT(operator);
+ return;
+ }
+ else if (parse_possible_directive(parser, "match") || parse_possible_directive(parser, "overload")) {
+ AstDirectiveAddOverload *add_overload = make_node(AstDirectiveAddOverload, Ast_Kind_Directive_Add_Overload);
+ add_overload->token = dir_token;
+
+ 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;
+ }
+
+ parser->parse_calls = 0;
+ add_overload->overloaded_function = (AstNode *) parse_expression(parser, 0);
+ parser->parse_calls = 1;
+
+ // Allow for
+ // #match
+ // something :: (....) {
+ // }
+ //
+ // This will make converting something to a overloaded
+ // function easier and require less copying by the programmer.
+ if (next_tokens_are(parser, 2, ':', ':')) {
+ consume_tokens(parser, 2);
+ }
+
+ add_overload->overload = parse_expression(parser, 0);
+
+ ENTITY_SUBMIT(add_overload);
+ return;
+ }
+ else if (parse_possible_directive(parser, "inject")) {
+ AstInjection *inject = make_node(AstInjection, Ast_Kind_Injection);
+ inject->token = dir_token;
+
+ parser->parse_calls = 0;
+ inject->full_loc = parse_expression(parser, 0);
+ parser->parse_calls = 1;
+
+ // See comment above
+ if (next_tokens_are(parser, 2, ':', ':')) {
+ consume_tokens(parser, 2);
+ }
+
+ inject->to_inject = parse_expression(parser, 0);
+
+ ENTITY_SUBMIT(inject);
+ return;
+ }
+ else if (parse_possible_directive(parser, "export")) {
+ AstDirectiveExport *export = make_node(AstDirectiveExport, Ast_Kind_Directive_Export);
+ export->token = dir_token;
+ parser->parse_calls = 0;
+ export->export_name_expr = parse_expression(parser, 0); // expect_token(parser, Token_Type_Literal_String);
+ parser->parse_calls = 1;
+
+ export->export = parse_expression(parser, 0);
+
+ ENTITY_SUBMIT(export);
+ return;
+ }
+ else if (parse_possible_directive(parser, "thread_local")) {
+ OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
+ AstMemRes* memres = parse_memory_reservation(parser, symbol, 1);
+
+ binding = make_node(AstBinding, Ast_Kind_Binding);
+ binding->token = symbol;
+ binding->flags |= private_kind;
+ binding->node = (AstNode *) memres;
+
+ goto submit_binding_to_entities;
+ }
+ else if (parse_possible_directive(parser, "init")) {
+ // :LinearTokenDependent
+ parse_init_directive(parser, parser->curr - 2);
+ return;
+ }
+ else if (parse_possible_directive(parser, "library")) {
+ // :LinearTokenDependent
+ AstDirectiveLibrary *library = make_node(AstDirectiveLibrary, Ast_Kind_Directive_Library);
+ library->token = parser->curr - 2;
+ library->library_symbol = parse_expression(parser, 0);
+
+ ENTITY_SUBMIT(library);
+ return;
+ }
+ else if (parse_possible_directive(parser, "tag")) {
+ parser->tag_depth += 1;
+
+ AstTyped *expr = parse_expression(parser, 0);
+ bh_arr_push(parser->stored_tags, expr);
+
+ parser->tag_depth -= 1;
+ return;
+ }
+ else {
+ OnyxToken* directive_token = expect_token(parser, '#');
+ OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
+
+ onyx_report_error(directive_token->pos, Error_Critical, "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, parser->curr->pos);
+ }
+
+ 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, package_node->token->pos);
+
+ 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.current_symbol_stack = NULL;
+ parser.current_function_stack = NULL;
+ parser.scope_flags = NULL;
+ parser.stored_tags = NULL;
+ parser.parse_calls = 1;
+ parser.tag_depth = 0;
+
+ 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.current_symbol_stack, 4);
+ bh_arr_new(global_heap_allocator, parser.scope_flags, 4);
+ bh_arr_new(global_heap_allocator, parser.stored_tags, 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;
+
+ parse_top_level_statements_until(parser, Token_Type_End_Stream);
+
+ parser->current_scope = parser->current_scope->parent;
+}
--- /dev/null
+
+//
+// Polymorphic Procedures
+//
+
+// This flag is used by some of the procedures that try working with polymorphic things,
+// but need to wait until more information is known. Instead of passing a out parameter
+// into each of these procedures, a single global variable is used instead. If the type
+// checker ever gets multi-threaded, this would have to become a threadlocal variable.
+static b32 flag_to_yield = 0;
+
+// This flag is used in the very special case that you are passing a polymorphic procedure
+// to a polymorphic procedure, and you have enough information to instantiate said procedure
+// in order to resolve the type of one of the return values.
+static b32 doing_nested_polymorph_lookup = 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 };
+AstTyped node_that_signals_failure = { Ast_Kind_Error, 0 };
+
+static void ensure_polyproc_cache_is_created(AstFunction* pp) {
+ if (pp->concrete_funcs == NULL) sh_new_arena(pp->concrete_funcs);
+ if (pp->active_queries.hashes == NULL) bh_imap_init(&pp->active_queries, global_heap_allocator, 31);
+}
+
+void insert_poly_sln_into_scope(Scope* scope, AstPolySolution *sln) {
+ 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;
+ ((AstTypeRawAlias *) node)->type = &basic_types[Basic_Kind_Type_Index];
+ ((AstTypeRawAlias *) node)->type_id = sln->type->id;
+ 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);
+}
+
+static void insert_poly_slns_into_scope(Scope* scope, bh_arr(AstPolySolution) slns) {
+ bh_arr_each(AstPolySolution, sln, slns) {
+ insert_poly_sln_into_scope(scope, sln);
+ }
+}
+
+// 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) {
+ solidified_func->func->flags |= Ast_Flag_Function_Used;
+ solidified_func->func->flags |= Ast_Flag_From_Polymorphism;
+
+ Entity func_header_entity = {
+ .state = Entity_State_Resolve_Symbols,
+ .type = Entity_Type_Function_Header,
+ .function = solidified_func->func,
+ .package = NULL,
+ .scope = solidified_func->func->poly_scope,
+ };
+
+ Entity func_entity = {
+ .state = Entity_State_Resolve_Symbols,
+ .type = Entity_Type_Function,
+ .function = solidified_func->func,
+ .package = NULL,
+ .scope = solidified_func->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_header_entity = entity_header;
+ solidified_func->func->entity_header = entity_header;
+ solidified_func->func->entity_body = entity_body;
+ solidified_func->func->entity = 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(
+ AstFunction* pp,
+ bh_arr(AstPolySolution) slns,
+ OnyxToken* tkn,
+ b32 header_only) {
+
+ AstSolidifiedFunction solidified_func;
+ solidified_func.func_header_entity = NULL;
+
+ // 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;
+
+ if (header_only) {
+ solidified_func.func = (AstFunction *) clone_function_header(context.ast_alloc, pp);
+ solidified_func.func->flags |= Ast_Flag_Incomplete_Body;
+
+ } else {
+ solidified_func.func = (AstFunction *) ast_clone(context.ast_alloc, pp);
+ }
+
+ solidified_func.func->poly_scope = scope_create(context.ast_alloc, pp->parent_scope_of_poly_proc, poly_scope_pos);
+ insert_poly_slns_into_scope(solidified_func.func->poly_scope, slns);
+
+ 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(AstFunction* pp, AstSolidifiedFunction *solidified_func) {
+ if (solidified_func->func->flags & Ast_Flag_Incomplete_Body) {
+ clone_function_body(context.ast_alloc, solidified_func->func, pp);
+
+ // 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));
+
+ 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_Address_Of: {
+ if (elem.actual->kind != Type_Kind_Pointer) break;
+
+ bh_arr_push(elem_queue, ((PolySolveElem) {
+ .type_expr = (AstType *) ((AstAddressOf *) elem.type_expr)->expr,
+ .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 && elem.actual->kind != Type_Kind_DynArray
+ && elem.actual->kind != Type_Kind_VarArgs && elem.actual->kind != Type_Kind_Array) break;
+
+ bh_arr_push(elem_queue, ((PolySolveElem) {
+ .type_expr = ((AstSliceType *) elem.type_expr)->elem,
+ .kind = PSK_Type,
+
+ // HACK: This makes the assumption that arrays, slices, dynamic arrays and varargs have the same element type at the same location.
+ .actual = elem.actual->Slice.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.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_Call: {
+ AstPolyCallType *pct = convert_call_to_polycall((AstCall *) elem.type_expr);
+ elem.type_expr = (AstType *) pct;
+
+ // fallthrough
+ }
+
+ 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(AstFunction* func, AstPolyParam* param, Arguments* args, char** err_msg) {
+ bh_arr(AstTyped *) arg_arr = args->values;
+ bh_arr(AstNamedValue *) named_values = args->named_values;
+
+ if ((i32) param->idx < 0)
+ return NULL;
+
+ // 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 = 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;
+ }
+ }
+
+ if (param->idx <= (u64) bh_arr_length(func->params)) {
+ if (func->params[param->idx].default_value) {
+ return (AstTyped *) func->params[param->idx].default_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;
+}
+
+static AstTyped* try_lookup_based_on_partial_function_type(AstFunction *pp, AstFunctionType *ft) {
+ if (ft->partial_function_type == NULL) {
+ AstType *old_return_type = ft->return_type;
+ ft->return_type = (AstType *) &basic_type_void;
+ ft->partial_function_type = type_build_from_ast(context.ast_alloc, (AstType *) ft);
+ ft->return_type = old_return_type;
+ if (!ft->partial_function_type) {
+ doing_nested_polymorph_lookup = 1;
+ return NULL;
+ }
+
+ assert(ft->partial_function_type);
+ }
+
+ AstTyped *result = (AstTyped *) polymorphic_proc_lookup(pp, PPLM_By_Function_Type, ft->partial_function_type, pp->token);
+ if (result && result->type == NULL) {
+ doing_nested_polymorph_lookup = 1;
+ result = NULL;
+ }
+ if (result == &node_that_signals_a_yield) {
+ doing_nested_polymorph_lookup = 1;
+ result = NULL;
+ }
+
+ return result;
+}
+
+// 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, AstFunction* func, 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(func, param, args, err_msg);
+ if (typed_param == NULL) return;
+
+ // CLEANUP FIXME HACK TODO GROSS
+ if (typed_param->kind == Ast_Kind_Argument) {
+ AstTyped* potential = ((AstArgument *) typed_param)->value;
+ if (potential->kind == Ast_Kind_Polymorphic_Proc) {
+ if (param->idx < (u32) bh_arr_length(func->params)) {
+ AstType *param_type = func->params[param->idx].local->type_node;
+ if (param_type->kind == Ast_Kind_Function_Type) {
+ AstFunctionType *ft = (AstFunctionType *) param_type;
+ b32 all_types = 1;
+ fori (i, 0, (i32) ft->param_count) {
+ if (!node_is_type((AstNode *) ft->params[i])) {
+ all_types = 0;
+ break;
+ }
+ }
+
+ if (all_types) {
+ typed_param = try_lookup_based_on_partial_function_type((AstFunction *) potential, ft);
+ }
+ }
+ }
+ }
+ }
+
+ 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;
+ }
+
+ PolySolveResult res = solve_poly_type(param->poly_sym, param->type_expr, actual_type);
+ if (res.kind == PSK_Undefined) {
+ *err_msg = bh_aprintf(global_scratch_allocator,
+ "Unable to solve for polymorphic variable '%b', given the type '%s'.",
+ param->poly_sym->token->text,
+ param->poly_sym->token->length,
+ type_get_name(actual_type));
+ }
+
+ *resolved = res;
+}
+
+
+// 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. In other words, this handles the ($Baked: type) case.
+// 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, AstFunction* func, 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(func, 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;
+ }
+
+ Type* param_type = NULL;
+ AstType *param_type_expr = func->params[param->idx].local->type_node;
+ 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;
+ }
+
+ param_type = type_build_from_ast(context.ast_alloc, param_type_expr);
+ if (param_type == NULL) {
+ flag_to_yield = 1;
+ *err_msg = "Waiting to know type for polymorphic value.";
+ return;
+ }
+
+ AstTyped* value_to_use = value;
+ if (value->kind == Ast_Kind_Macro) {
+ value_to_use = (AstTyped *) get_function_from_node((AstNode *) value);
+ }
+
+ TypeMatch tm = unify_node_and_type(&value_to_use, param_type);
+ if (tm == TYPE_MATCH_FAILED) {
+ 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(func),
+ type_get_name(param_type),
+ param->idx + 1,
+ bh_num_suffix(param->idx + 1),
+ node_get_type_name(value_to_use));
+ return;
+ }
+
+ if (tm == TYPE_MATCH_YIELD) flag_to_yield = 1;
+
+ *resolved = ((PolySolveResult) { PSK_Value, value });
+ }
+
+ if (orig_value->kind == Ast_Kind_Argument) {
+ ((AstArgument *) orig_value)->is_baked = 1;
+ }
+}
+
+TypeMatch find_polymorphic_sln(AstPolySolution *out, AstPolyParam *param, AstFunction *func, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
+ // 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, func, param, pp_lookup, actual, err_msg); break;
+ case PPK_Baked_Value: solve_for_polymorphic_param_value(&resolved, func, param, pp_lookup, actual, err_msg); break;
+
+ default: if (err_msg) *err_msg = "Invalid polymorphic parameter kind. This is a compiler bug.";
+ }
+
+ if (doing_nested_polymorph_lookup) {
+ doing_nested_polymorph_lookup = 0;
+ return TYPE_MATCH_SPECIAL;
+ }
+
+ if (flag_to_yield) {
+ flag_to_yield = 0;
+ return TYPE_MATCH_YIELD;
+ }
+
+ switch (resolved.kind) {
+ case PSK_Type:
+ out->kind = PSK_Type;
+ out->poly_sym = param->poly_sym;
+ out->type = resolved.actual;
+ return TYPE_MATCH_SUCCESS;
+
+ case PSK_Value:
+ out->kind = PSK_Value;
+ out->poly_sym = param->poly_sym;
+ out->value = resolved.value;
+ return TYPE_MATCH_SUCCESS;
+
+ case PSK_Undefined:
+ default:
+ // 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);
+
+ out->kind = PSK_Undefined;
+ return TYPE_MATCH_FAILED;
+ }
+}
+
+// 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(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken *tkn, b32 necessary) {
+ ensure_polyproc_cache_is_created(pp);
+ if (bh_imap_has(&pp->active_queries, (u64) actual)) {
+ AstPolyQuery *query = (AstPolyQuery *) bh_imap_get(&pp->active_queries, (u64) actual);
+ assert(query->kind == Ast_Kind_Polymorph_Query);
+ assert(query->entity);
+
+ if (query->entity->state == Entity_State_Finalized) return query->slns;
+ if (query->entity->state == Entity_State_Failed) return NULL;
+
+ flag_to_yield = 1;
+ return NULL;
+ }
+
+ 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);
+
+ AstPolyQuery *query = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyQuery), Ast_Kind_Polymorph_Query);
+ query->token = pp->token;
+ query->proc = pp;
+ query->pp_lookup = pp_lookup;
+ query->given = actual;
+ query->error_loc = tkn;
+ query->slns = slns;
+ query->function_header = clone_function_header(context.ast_alloc, pp);
+ query->function_header->flags |= Ast_Flag_Header_Check_No_Error;
+ query->function_header->scope = NULL;
+ query->error_on_fail = necessary;
+ query->successful_symres = 1;
+
+ bh_imap_put(&pp->active_queries, (u64) actual, (u64) query);
+ add_entities_for_node(NULL, (AstNode *) query, NULL, NULL);
+
+ flag_to_yield = 1;
+ 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(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn) {
+ ensure_polyproc_cache_is_created(pp);
+
+ bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, tkn, 1);
+ if (slns == NULL) {
+ if (flag_to_yield) {
+ flag_to_yield = 0;
+ return (AstFunction *) &node_that_signals_a_yield;
+ }
+
+ return NULL;
+ }
+
+ AstFunction* result = polymorphic_proc_solidify(pp, slns, tkn);
+ return result;
+}
+
+AstFunction* polymorphic_proc_solidify(AstFunction* 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);
+ i32 index = shgeti(pp->concrete_funcs, unique_key);
+ if (index != -1) {
+ AstSolidifiedFunction solidified_func = pp->concrete_funcs[index].value;
+
+ // 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);
+ add_solidified_function_entities(&solidified_func);
+
+ // NOTE: Cache the function for later use, reducing duplicate functions.
+ shput(pp->concrete_funcs, unique_key, solidified_func);
+
+ return (AstFunction *) &node_that_signals_a_yield;
+}
+
+// NOTE: This can return either a AstFunction or an AstFunction, depending if enough parameters were
+// supplied to remove all the polymorphic variables from the function.
+AstNode* polymorphic_proc_try_solidify(AstFunction* 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 {
+ if (pp->name) {
+ onyx_report_error(tkn->pos, Error_Critical, "'%b' is not a type variable of '%s'.",
+ sln->poly_sym->token->text, sln->poly_sym->token->length, pp->name);
+ } else {
+ onyx_report_error(tkn->pos, Error_Critical, "'%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
+ AstFunction* new_pp = onyx_ast_node_new(context.ast_alloc, sizeof(AstFunction), Ast_Kind_Polymorphic_Proc);
+ memcpy(new_pp, pp, sizeof(AstFunction));
+ new_pp->token = tkn;
+ new_pp->poly_params = bh_arr_copy(context.ast_alloc, 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(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual) {
+ ensure_polyproc_cache_is_created(pp);
+ bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, NULL, 0);
+ if (flag_to_yield) {
+ flag_to_yield = 0;
+ return (AstFunction *) &node_that_signals_a_yield;
+ }
+ if (slns == NULL) return NULL;
+
+ ensure_polyproc_cache_is_created(pp);
+
+ return polymorphic_proc_build_only_header_with_slns(pp, slns, 0);
+}
+
+AstFunction* polymorphic_proc_build_only_header_with_slns(AstFunction* pp, bh_arr(AstPolySolution) slns, b32 error_if_failed) {
+ AstSolidifiedFunction solidified_func;
+
+ char* unique_key = build_poly_slns_unique_key(slns);
+ i32 index = shgeti(pp->concrete_funcs, unique_key);
+ if (index != -1) {
+ solidified_func = pp->concrete_funcs[index].value;
+
+ } 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.func_header_entity) {
+ if (solidified_func.func_header_entity->state == Entity_State_Finalized) return solidified_func.func;
+ if (solidified_func.func_header_entity->state == Entity_State_Failed) return NULL;
+
+ return (AstFunction *) &node_that_signals_a_yield;
+ }
+
+ BH_MASK_SET(solidified_func.func->flags, !error_if_failed, Ast_Flag_Header_Check_No_Error);
+
+ Entity func_header_entity = {
+ .state = Entity_State_Resolve_Symbols,
+ .type = Entity_Type_Temp_Function_Header,
+ .function = solidified_func.func,
+ .package = NULL,
+ .scope = solidified_func.func->poly_scope,
+ };
+
+ Entity* func_header_entity_ptr = entity_heap_insert(&context.entities, func_header_entity);
+ solidified_func.func_header_entity = func_header_entity_ptr;
+
+ // NOTE: Cache the function for later use.
+ shput(pp->concrete_funcs, unique_key, solidified_func);
+
+ return (AstFunction *) &node_that_signals_a_yield;
+}
+
+typedef struct AutoPolymorphVariable {
+ u32 idx;
+ u32 variable_count;
+ AstType *base_type;
+ AstType **replace;
+} AutoPolymorphVariable;
+
+// This should be called after all the parameter types have been symresed, but before anything
+// happens to the body.
+b32 potentially_convert_function_to_polyproc(AstFunction *func) {
+ bh_arr(AutoPolymorphVariable) auto_vars = NULL;
+ bh_arr_new(global_heap_allocator, auto_vars, 2);
+
+ u32 param_idx = 0;
+ bh_arr_each(AstParam, param, func->params) {
+ AstType **to_replace = ¶m->local->type_node;
+ AstType *param_type = param->local->type_node;
+
+ b32 done = 0;
+ while (!done && param_type) {
+ switch (param_type->kind) {
+ case Ast_Kind_Pointer_Type: to_replace = &((AstPointerType *) *to_replace)->elem; param_type = ((AstPointerType *) param_type)->elem; break;
+ case Ast_Kind_Array_Type: to_replace = &((AstArrayType *) *to_replace)->elem; param_type = ((AstArrayType *) param_type)->elem; break;
+ case Ast_Kind_Slice_Type: to_replace = &((AstSliceType *) *to_replace)->elem; param_type = ((AstSliceType *) param_type)->elem; break;
+ case Ast_Kind_DynArr_Type: to_replace = &((AstDynArrType *) *to_replace)->elem; param_type = ((AstDynArrType *) param_type)->elem; break;
+ case Ast_Kind_Alias: param_type = (AstType *) ((AstAlias *) param_type)->alias; break;
+ case Ast_Kind_Type_Alias: param_type = ((AstTypeAlias *) param_type)->to; break;
+ case Ast_Kind_Poly_Struct_Type: {
+ AutoPolymorphVariable apv;
+ apv.idx = param_idx;
+ apv.base_type = param->local->type_node;
+ apv.variable_count = bh_arr_length(((AstPolyStructType *) param_type)->poly_params);
+ apv.replace = to_replace;
+
+ bh_arr_push(auto_vars, apv);
+ done = 1;
+ break;
+ }
+
+ default: done = 1; break;
+ }
+ }
+
+ param_idx++;
+ }
+
+ if (bh_arr_length(auto_vars) == 0) return 0;
+
+ param_idx = 0;
+ bh_arr_each(AutoPolymorphVariable, apv, auto_vars) {
+ AstPolyParam pp;
+ pp.idx = apv->idx;
+ pp.kind = PPK_Poly_Type;
+
+ AstPolyCallType* pcall = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyCallType), Ast_Kind_Poly_Call_Type);
+ pcall->callee = *apv->replace;
+ pcall->token = pcall->callee->token;
+ bh_arr_new(global_heap_allocator, pcall->params, apv->variable_count);
+
+ if (apv->base_type->kind == Ast_Kind_Poly_Struct_Type) {
+ pp.type_expr = (AstType *) pcall;
+ } else {
+ pp.type_expr = apv->base_type;
+ }
+ *apv->replace = (AstType *) pcall;
+
+ fori (i, 0, apv->variable_count) {
+ OnyxToken* name_token = bh_alloc_item(context.ast_alloc, OnyxToken);
+ name_token->text = bh_aprintf(context.ast_alloc, "__autopoly_var_%d\0", param_idx);
+ name_token->length = strlen(name_token->text);
+ name_token->type = Token_Type_Symbol;
+ name_token->pos = pcall->token->pos;
+
+ pp.poly_sym = make_symbol(context.ast_alloc, name_token);
+ bh_arr_push(pcall->params, pp.poly_sym);
+ bh_arr_push(func->poly_params, pp);
+ param_idx ++;
+ }
+ }
+
+ convert_function_to_polyproc(func);
+
+ bh_arr_each(AstParam, param, func->params) {
+ param->local->flags |= Ast_Flag_Param_Symbol_Dirty;
+ }
+
+ return 1;
+}
+
+//
+// Polymorphic Structures
+//
+//
+// Currently, I am not very happy about how polymorphic structure generation works. My biggest problem
+// with it is that it is very different from the polymorhic procedure generation. Also, it needs to
+// completely generate and check the structure right away, which means there is a lot of up-front work
+// done here that could probably be done elsewhere. This really relates to a large problem in the compiler
+// that types need to be known completely by the time symbol resolution is done, even though that
+// information shouldn't need to be known until right before the types are checked.
+//
+// The above documentation is very incorrect but I don't want to fix it right now. Basically, polymorphic
+// structures now have a delay instantiation phase and are not forced to be completed immediately.
+
+char* build_poly_struct_name(AstPolyStructType* ps_type, Type* cs_type) {
+ char name_buf[256];
+ fori (i, 0, 256) name_buf[i] = 0;
+
+ strncat(name_buf, ps_type->name, 255);
+ strncat(name_buf, "(", 255);
+ bh_arr_each(AstPolySolution, ptype, cs_type->Struct.poly_sln) {
+ if (ptype != cs_type->Struct.poly_sln)
+ strncat(name_buf, ", ", 255);
+
+ // This logic will have to be other places as well.
+
+ switch (ptype->kind) {
+ case PSK_Undefined: assert(0); break;
+ case PSK_Type: strncat(name_buf, type_get_name(ptype->type), 255); break;
+ case PSK_Value: {
+ // FIX
+ AstNode* value = strip_aliases((AstNode *) ptype->value);
+
+ if (value->kind == Ast_Kind_NumLit) {
+ AstNumLit* nl = (AstNumLit *) value;
+ if (type_is_integer(nl->type)) {
+ strncat(name_buf, bh_bprintf("%l", nl->value.l), 127);
+ } else {
+ strncat(name_buf, "numlit (FIX ME)", 127);
+ }
+ } else if (value->kind == Ast_Kind_Code_Block) {
+ AstCodeBlock* code = (AstCodeBlock *) value;
+ OnyxFilePos code_loc = code->token->pos;
+ strncat(name_buf, bh_bprintf("code at %s:%d,%d", code_loc.filename, code_loc.line, code_loc.column), 127);
+ } else {
+ strncat(name_buf, "<expr>", 127);
+ }
+
+ break;
+ }
+ }
+ }
+ strncat(name_buf, ")", 255);
+
+ return bh_aprintf(global_heap_allocator, "%s", name_buf);
+}
+
+Type* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos, b32 error_if_failed) {
+ // @Cleanup
+ assert(ps_type->scope != NULL);
+
+ if (ps_type->concrete_structs == NULL) {
+ sh_new_arena(ps_type->concrete_structs);
+ }
+
+ if (bh_arr_length(slns) != bh_arr_length(ps_type->poly_params)) {
+ onyx_report_error(pos, Error_Critical, "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];
+ i++;
+ }
+
+ char* unique_key = build_poly_slns_unique_key(slns);
+ i32 index = shgeti(ps_type->concrete_structs, unique_key);
+ if (index != -1) {
+ AstStructType* concrete_struct = ps_type->concrete_structs[index].value;
+
+ if (concrete_struct->entity_type->state < Entity_State_Check_Types) {
+ return NULL;
+ }
+
+ if (concrete_struct->entity_type->state == Entity_State_Failed) {
+ return (Type *) &node_that_signals_failure;
+ }
+
+ 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 cs_type;
+ }
+
+ 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);
+ concrete_struct->polymorphic_error_loc = pos;
+ BH_MASK_SET(concrete_struct->flags, !error_if_failed, Ast_Flag_Header_Check_No_Error);
+
+
+ i64 arg_count = bh_arr_length(ps_type->poly_params);
+ bh_arr_new(global_heap_allocator, concrete_struct->polymorphic_argument_types, arg_count);
+ bh_arr_set_length(concrete_struct->polymorphic_argument_types, arg_count);
+ concrete_struct->polymorphic_arguments = bh_arr_copy(global_heap_allocator, slns);
+
+ fori (i, 0, (i64) bh_arr_length(ps_type->poly_params)) {
+ concrete_struct->polymorphic_argument_types[i] = (AstType *) ast_clone(context.ast_alloc, ps_type->poly_params[i].type_node);
+ }
+
+ shput(ps_type->concrete_structs, unique_key, concrete_struct);
+ add_entities_for_node(NULL, (AstNode *) concrete_struct, sln_scope, NULL);
+ return NULL;
+}
--- /dev/null
+#define BH_DEBUG
+#include "parser.h"
+#include "utils.h"
+#include "astnodes.h"
+#include "errors.h"
+
+// :EliminatingSymres - notes the places where too much work is being done in symbol resolution
+
+// Variables used during the symbol resolution phase.
+static Scope* curr_scope = NULL;
+static b32 report_unresolved_symbols = 1;
+static b32 resolved_a_symbol = 0;
+
+// 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)
+
+#define SYMRES_INVISIBLE(kind, node, ...) do { \
+ (node)->flags |= Ast_Flag_Symbol_Invisible; \
+ SymresStatus ss = symres_ ## kind (__VA_ARGS__); \
+ (node)->flags &= ~Ast_Flag_Symbol_Invisible; \
+ if (ss > Symres_Errors_Start) return ss; \
+ } while (0)
+
+typedef enum SymresStatus {
+ Symres_Success,
+ Symres_Complete,
+ Symres_Goto_Parse,
+
+ 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** pcall);
+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_case(AstSwitchCase *casenode);
+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 SymresStatus symres_constraint(AstConstraint* constraint);
+static SymresStatus symres_polyquery(AstPolyQuery *query);
+
+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) {
+ token_toggle_end(token);
+ char *closest = find_closest_symbol_in_scope_and_parents(curr_scope, token->text);
+ token_toggle_end(token);
+
+ if (closest) onyx_report_error(token->pos, Error_Critical, "Unable to resolve symbol '%b'. Did you mean '%s'?", token->text, token->length, closest);
+ else onyx_report_error(token->pos, Error_Critical, "Unable to resolve symbol '%b'.", token->text, token->length);
+
+ return Symres_Error;
+ } else {
+ return Symres_Yield_Macro;
+ }
+
+ } else {
+ *symbol_node = res;
+ resolved_a_symbol = 1;
+ }
+
+ 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;
+ s_node->flags |= Ast_Flag_Comptime;
+
+ if (s_node->scope) {
+ // FIX: This is probably wrong for the long term.
+ s_node->scope->parent = curr_scope;
+
+ scope_enter(s_node->scope);
+ }
+
+ if (s_node->min_size_) SYMRES(expression, &s_node->min_size_);
+ if (s_node->min_alignment_) SYMRES(expression, &s_node->min_alignment_);
+
+ if (s_node->polymorphic_argument_types) {
+ assert(s_node->polymorphic_arguments);
+
+ SymresStatus ss = Symres_Success, result;
+ fori (i, 0, (i64) bh_arr_length(s_node->polymorphic_argument_types)) {
+ result = symres_type(&s_node->polymorphic_argument_types[i]);
+ if (result > ss) ss = result;
+
+ if (s_node->polymorphic_arguments[i].value) {
+ result = symres_expression(&s_node->polymorphic_arguments[i].value);
+ if (result > ss) ss = result;
+ }
+ }
+ }
+
+ if (s_node->constraints.constraints) {
+ bh_arr_each(AstConstraint *, constraint, s_node->constraints.constraints) {
+ SYMRES(constraint, *constraint);
+ }
+ }
+
+ 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 (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);
+ 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;
+
+ if (ftype->param_count > 0) {
+ fori (i, 0, (i64) ftype->param_count) {
+ SYMRES(type, &ftype->params[i]);
+ }
+ }
+
+ SYMRES(type, &ftype->return_type);
+ 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;
+
+ if (pst_node->scope == NULL) {
+ pst_node->scope = scope_create(context.ast_alloc, pst_node->entity->scope, pst_node->token->pos);
+ }
+ 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_INVISIBLE(type, alias, (AstType **) &alias->alias);
+
+ break;
+ }
+
+ case Ast_Kind_Typeof: {
+ AstTypeOf* type_of = (AstTypeOf *) *type;
+ SYMRES(expression, &type_of->expr);
+ break;
+ }
+
+ case Ast_Kind_Distinct_Type: {
+ AstDistinctType *distinct = (AstDistinctType *) *type;
+ SYMRES(type, &distinct->base_type);
+ 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** pcall) {
+ AstCall *call = *pcall;
+ SYMRES(expression, (AstTyped **) &call->callee);
+ SYMRES(arguments, &call->args);
+
+ AstNode* callee = strip_aliases((AstNode *) call->callee);
+ if (callee->kind == Ast_Kind_Poly_Struct_Type) {
+ *pcall = (AstCall *) convert_call_to_polycall(call);
+ SYMRES(type, (AstType **) pcall);
+ return Symres_Success;
+ }
+
+ 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);
+
+ b32 force_a_lookup = 0;
+
+ if (expr->kind == Ast_Kind_Enum_Type || expr->kind == Ast_Kind_Type_Raw_Alias) {
+ force_a_lookup = 1;
+ }
+
+ //
+ // If we are trying to access a field on an alias, we have to make sure
+ // the alias is "ready" to have a symbol looked up inside of it. This means
+ // the alias should have passed symbol resolution. If not, force a lookup
+ // and yield if the alias was not ready.
+ if ((*fa)->expr->kind == Ast_Kind_Alias) {
+ assert((*fa)->expr->entity);
+ if ((*fa)->expr->entity->state < Entity_State_Check_Types) {
+ force_a_lookup = 1;
+ }
+ }
+
+ 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) {
+ token_toggle_end((*fa)->token);
+ char *closest = find_closest_symbol_in_node((AstNode *) expr, (*fa)->token->text);
+ token_toggle_end((*fa)->token);
+
+ AstPackage *package = (AstPackage *) strip_aliases((AstNode *) (*fa)->expr);
+
+ if (closest) {
+ onyx_report_error((*fa)->token->pos, Error_Critical, "'%b' was not found in package '%s'. Did you mean '%s'?",
+ (*fa)->token->text,
+ (*fa)->token->length,
+ package->package->name,
+ closest);
+ } else {
+ onyx_report_error((*fa)->token->pos, Error_Critical, "'%b' was not found in package '%s'. Perhaps it is defined in a file that wasn't loaded?",
+ (*fa)->token->text,
+ (*fa)->token->length,
+ package->package->name);
+ }
+ return Symres_Error;
+
+ } else {
+ return Symres_Yield_Macro;
+ }
+ }
+ else if (force_a_lookup) {
+ if (context.cycle_detected) {
+ onyx_report_error((*fa)->token->pos, Error_Critical, "'%b' does not exist here. This is a bad error message.",
+ (*fa)->token->text,
+ (*fa)->token->length);
+ return Symres_Error;
+ }
+
+ 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, Error_Critical, "Pipe operator expected call on right side.");
+ return Symres_Error;
+ }
+
+ if ((*pipe)->left == NULL) return Symres_Error;
+
+ // :EliminatingSymres
+ 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) {
+ SYMRES(expression, &(*mcall)->left);
+ if ((*mcall)->left == NULL) return Symres_Error;
+
+ // :EliminatingSymres
+ if (((*mcall)->flags & Ast_Flag_Has_Been_Symres) == 0) {
+ if ((*mcall)->right->kind != Ast_Kind_Call) {
+ onyx_report_error((*mcall)->token->pos, Error_Critical, "'->' expected procedure call on right side.");
+ return Symres_Error;
+ }
+
+ AstFieldAccess* implicit_field_access = make_field_access(context.ast_alloc, (*mcall)->left, NULL);
+ implicit_field_access->token = ((AstCall *) (*mcall)->right)->callee->token;
+ ((AstCall *) (*mcall)->right)->callee = (AstTyped *) implicit_field_access;
+ (*mcall)->flags |= Ast_Flag_Has_Been_Symres;
+ }
+
+ assert((*mcall)->right->kind == Ast_Kind_Call);
+ SYMRES(expression, (AstTyped **) &(*mcall)->right);
+
+ 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);
+
+ // :EliminatingSymres
+ 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);
+
+ // :EliminatingSymres
+ 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_address_of(AstAddressOf** paof) {
+ AstAddressOf *aof = (AstAddressOf *) *paof;
+ SYMRES(expression, &aof->expr);
+
+ AstTyped *expr = (AstTyped *) strip_aliases((AstNode *) aof->expr);
+ if (node_is_type((AstNode *) expr)) {
+ AstPointerType *pt = onyx_ast_node_new(context.ast_alloc, sizeof(AstPointerType), Ast_Kind_Pointer_Type);
+ pt->token = aof->token;
+ pt->elem = (AstType *) expr;
+ pt->__unused = aof->next;
+ *paof = (AstAddressOf *) pt;
+ SYMRES(type, (AstType **) &pt);
+ return Symres_Success;
+ }
+
+ 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_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_Address_Of: SYMRES(address_of, (AstAddressOf **) expr); break;
+ case Ast_Kind_Alias: {
+ AstAlias *alias = (AstAlias *) *expr;
+ SYMRES_INVISIBLE(expression, alias, &alias->alias);
+ break;
+ }
+
+ case Ast_Kind_Range_Literal:
+ SYMRES(expression, &((AstRangeLiteral *)(*expr))->low);
+ SYMRES(expression, &((AstRangeLiteral *)(*expr))->high);
+
+ // :EliminatingSymres
+ SYMRES(type, &builtin_range_type);
+ (*expr)->type_node = builtin_range_type;
+ break;
+
+ case Ast_Kind_Function:
+ case Ast_Kind_NumLit:
+ SYMRES(type, &(*expr)->type_node);
+ break;
+
+ case Ast_Kind_StrLit: {
+ AstStrLit* str = (AstStrLit *) *expr;
+ if (str->is_cstr) {
+ SYMRES(type, &builtin_cstring_type);
+ str->type_node = builtin_cstring_type;
+
+ } else {
+ SYMRES(type, &builtin_string_type);
+ str->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;
+
+ case Ast_Kind_Param:
+ if ((*expr)->flags & Ast_Flag_Param_Symbol_Dirty) {
+ assert((*expr)->token->type == Token_Type_Symbol);
+ *expr = (AstTyped *) make_symbol(context.ast_alloc, (*expr)->token);
+ SYMRES(expression, expr);
+ }
+ 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) {
+ if (context.cycle_detected) {
+ onyx_report_error(ifnode->token->pos, Error_Waiting_On, "Waiting on static if resolution.");
+ return Symres_Error;
+ } else {
+ 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_case(AstSwitchCase *casenode) {
+ if (!casenode->is_default) {
+ bh_arr_each(AstTyped *, expr, casenode->values) {
+ SYMRES(expression, expr);
+ }
+ }
+
+ SYMRES(block, casenode->block);
+ 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);
+
+ if (switchnode->cases == NULL) {
+ SYMRES(block, switchnode->case_block);
+ } else {
+ bh_arr_each(AstSwitchCase *, pcase, switchnode->cases) {
+ SYMRES(case, *pcase);
+ }
+
+ if (switchnode->default_case) SYMRES(block, switchnode->default_case);
+ }
+
+ if (switchnode->switch_kind == Switch_Kind_Use_Equals && switchnode->case_exprs) {
+ bh_arr_each(CaseToBlock, ctb, switchnode->case_exprs) {
+ SYMRES(expression, (AstTyped **) &ctb->comparison);
+ }
+ }
+
+ 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);
+
+ AstTyped *use_expr = (AstTyped *) strip_aliases((AstNode *) use->expr);
+
+ // :EliminatingSymres
+ 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, Error_Critical,
+ "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);
+ }
+ }
+
+ if (!use->entity) {
+ add_entities_for_node(NULL, (AstNode *) use, curr_scope, NULL);
+ }
+
+ package_track_use_package(package->package, use->entity);
+ return Symres_Success;
+ }
+
+ if (use_expr->kind == Ast_Kind_Foreign_Block) {
+ AstForeignBlock* fb = (AstForeignBlock *) use_expr;
+ if (fb->entity->state <= Entity_State_Resolve_Symbols) return Symres_Yield_Macro;
+
+ if (fb->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, fb->scope, pos);
+
+ } else {
+ bh_arr_each(QualifiedUse, qu, use->only) {
+ AstNode* thing = symbol_resolve(fb->scope, qu->symbol_name);
+ if (thing == NULL) { // :SymresStall
+ if (report_unresolved_symbols) {
+ onyx_report_error(qu->symbol_name->pos, Error_Critical,
+ "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);
+ }
+ }
+
+ 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, Error_Critical,
+ "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;
+
+ // :EliminatingSymres
+ 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;
+
+ fori (i, 0, shlen(st->Struct.members)) {
+ StructMember* value = st->Struct.members[i].value;
+ AstFieldAccess* fa = make_field_access(context.ast_alloc, use_expr, value->name);
+ symbol_raw_introduce(curr_scope, value->name, use->token->pos, (AstNode *) fa);
+ }
+
+ return Symres_Success;
+ }
+
+cannot_use:
+ onyx_report_error(use->token->pos, Error_Critical, "Cannot use this because its type is unknown.");
+ return Symres_Error;
+}
+
+static SymresStatus symres_directive_solidify(AstDirectiveSolidify** psolid) {
+ AstDirectiveSolidify* solid = *psolid;
+
+ SYMRES(expression, (AstTyped **) &solid->poly_proc);
+ if (solid->poly_proc && solid->poly_proc->kind == Ast_Kind_Directive_Solidify) {
+ AstFunction* potentially_resolved_proc = (AstFunction *) ((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, Error_Critical, "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);
+ }
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_directive_defined(AstDirectiveDefined** pdefined) {
+ AstDirectiveDefined* defined = *pdefined;
+
+ b32 has_to_be_resolved = context.cycle_almost_detected;
+
+ resolved_a_symbol = 0;
+ SymresStatus ss = symres_expression(&defined->expr);
+ if (has_to_be_resolved && ss != Symres_Success && !resolved_a_symbol) {
+ // The symbol definitely was not found and there is no chance that it could be found.
+ defined->is_defined = 0;
+ return Symres_Success;
+ }
+
+ if (ss == Symres_Success) {
+ defined->is_defined = 1;
+ return Symres_Success;
+ }
+
+ return Symres_Yield_Macro;
+}
+
+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_Switch_Case: SYMRES(case, (AstSwitchCase *) *stmt); break;
+ case Ast_Kind_Jump: break;
+ case Ast_Kind_Directive_Remove: 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) {
+ AstNode** start = &block->body;
+ fori (i, 0, block->statement_idx) {
+ start = &(*start)->next;
+ }
+
+ b32 remove = 0;
+
+ while (*start) {
+ SymresStatus cs = symres_statement(start, &remove);
+
+ if (remove) {
+ remove = 0;
+ AstNode* tmp = (*start)->next;
+ (*start)->next = NULL;
+ (*start) = tmp;
+
+ } else {
+ switch (cs) {
+ case Symres_Success:
+ start = &(*start)->next;
+ block->statement_idx++;
+ break;
+
+ default:
+ return cs;
+ }
+ }
+ }
+
+ block->statement_idx = 0;
+ }
+
+ 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);
+
+ if (func->constraints.constraints != NULL && func->constraints.constraints_met == 0) {
+ bh_arr_each(AstConstraint *, constraint, func->constraints.constraints) {
+ SYMRES(constraint, *constraint);
+ }
+
+ // Return early here to finish checking constraints in the checker.
+ // Will resume here after constraints have been met.
+ return Symres_Success;
+ }
+
+ 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;
+ }
+ }
+
+ bh_arr_each(AstParam, param, func->params) {
+ symbol_introduce(curr_scope, param->local->token, (AstNode *) param->local);
+ }
+
+ bh_arr_each(AstParam, param, func->params) {
+ if (param->local->type_node != NULL) {
+ SYMRES_INVISIBLE(type, param->local, ¶m->local->type_node);
+ }
+ }
+
+ if (potentially_convert_function_to_polyproc(func)) {
+ return Symres_Complete;
+ }
+
+ if (func->nodes_that_need_entities_after_clone && bh_arr_length(func->nodes_that_need_entities_after_clone) > 0 && func->entity) {
+ bh_arr_each(AstNode *, node, func->nodes_that_need_entities_after_clone) {
+ // This makes a lot of assumptions about how these nodes are being processed,
+ // and I don't want to start using this with other nodes without considering
+ // what the ramifications of that is.
+ assert((*node)->kind == Ast_Kind_Static_If || (*node)->kind == Ast_Kind_File_Contents);
+
+ // Need to curr_scope->parent because curr_scope is the function body scope.
+ Scope *scope = curr_scope->parent;
+
+ if ((*node)->kind == Ast_Kind_Static_If) {
+ AstIf *static_if = (AstIf *) *node;
+ assert(static_if->defined_in_scope);
+ scope = static_if->defined_in_scope;
+
+ if (func->poly_scope) {
+ scope = scope_create(context.ast_alloc, scope, static_if->token->pos);
+ scope_include(scope, func->poly_scope, static_if->token->pos);
+ }
+ }
+
+ add_entities_for_node(NULL, *node, scope, func->entity->package);
+ }
+
+ bh_arr_set_length(func->nodes_that_need_entities_after_clone, 0);
+ }
+
+ SYMRES(type, &func->return_type);
+
+ scope_leave();
+
+ return Symres_Success;
+}
+
+SymresStatus symres_function(AstFunction* func) {
+ if (func->entity_header && func->entity_header->state < Entity_State_Check_Types) return Symres_Yield_Macro;
+ if (func->kind == Ast_Kind_Polymorphic_Proc) return Symres_Complete;
+ assert(func->scope);
+
+ scope_enter(func->scope);
+
+ if ((func->flags & Ast_Flag_Has_Been_Symres) == 0) {
+ // :EliminatingSymres
+ 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->is_used && !param->use_processed) {
+ 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) 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;
+ }
+
+ if (st->Struct.status != SPS_Uses_Done) return Symres_Yield_Macro;
+
+ fori (i, 0, shlen(st->Struct.members)) {
+ StructMember* value = st->Struct.members[i].value;
+ 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);
+ }
+
+ param->use_processed = 1;
+
+ } else if (param->local->type != NULL) {
+ onyx_report_error(param->local->token->pos, Error_Critical, "Can only 'use' structures or pointers to structures.");
+
+ } else {
+ // :ExplicitTyping
+ onyx_report_error(param->local->token->pos, Error_Critical, "Cannot deduce type of parameter '%b'; Try adding it explicitly.",
+ param->local->token->text,
+ param->local->token->length);
+ }
+ }
+ }
+
+ bh_arr_each(AstTyped *, pexpr, func->tags) {
+ SYMRES(expression, pexpr);
+ }
+
+ 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, Error_Critical,
+ "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;
+
+ if (enum_node->scope == NULL) {
+ enum_node->backing_type = type_build_from_ast(context.ast_alloc, enum_node->backing);
+ enum_node->scope = scope_create(context.ast_alloc, curr_scope, enum_node->token->pos);
+
+ type_build_from_ast(context.ast_alloc, (AstType *) enum_node);
+ }
+
+ scope_enter(enum_node->scope);
+
+ // :EliminatingSymres
+ u64 next_assign_value = enum_node->is_flags ? 1 : 0;
+ bh_arr_each(AstEnumValue *, value, enum_node->values) {
+ if ((*value)->flags & Ast_Flag_Has_Been_Checked) continue;
+
+ (*value)->type = enum_node->etcache;
+ (*value)->flags |= Ast_Flag_Comptime;
+
+ if ((*value)->value != NULL) {
+ SYMRES(expression, &(*value)->value);
+
+ if ((*value)->value->kind == Ast_Kind_Enum_Value) {
+ (*value)->value = ((AstEnumValue *) (*value)->value)->value;
+ (*value)->value->type = enum_node->etcache;
+ }
+
+ if ((*value)->value->kind == Ast_Kind_NumLit) {
+ AstNumLit *n_value = (AstNumLit *) (*value)->value;
+ resolve_expression_type((AstTyped *) n_value);
+
+ if (type_is_small_integer(n_value->type)) {
+ next_assign_value = n_value->value.i;
+ } else if (type_is_integer(n_value->type)) {
+ next_assign_value = n_value->value.l;
+ } else {
+ onyx_report_error((*value)->token->pos, Error_Critical, "expected numeric integer literal for enum initialization, got '%s'", type_get_name(n_value->type));
+ return Symres_Error;
+ }
+
+ n_value->type = enum_node->etcache;
+
+ } else {
+ if ((*value)->entity == NULL) {
+ add_entities_for_node(NULL, (AstNode *) (*value), enum_node->scope, NULL);
+ }
+
+ if (context.cycle_detected) {
+ onyx_report_error((*value)->token->pos, Error_Critical, "Expected compile time known value for enum initialization.");
+ return Symres_Error;
+ }
+
+ return Symres_Yield_Macro;
+ }
+
+ } else {
+ AstNumLit* num = make_int_literal(context.ast_alloc, next_assign_value);
+ num->type = enum_node->etcache;
+
+ (*value)->value = (AstTyped *) num;
+ }
+
+ symbol_introduce(enum_node->scope, (*value)->token, (AstNode *) (*value));
+
+ (*value)->flags |= Ast_Flag_Comptime | Ast_Flag_Has_Been_Checked;
+
+ if (enum_node->is_flags) {
+ next_assign_value <<= 1;
+ } else {
+ next_assign_value++;
+ }
+ }
+
+ scope_leave();
+
+ // HACK this ensure that you can only lookup symbols in an Enum that are actually defined in the enum.
+ // However, during the symbol resolution of the values in an enum, they need to be able to see the
+ // enclosing scope.
+ enum_node->scope->parent = NULL;
+
+ 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);
+
+ if (st->meta_tags) {
+ bh_arr_each(AstTyped *, meta, st->meta_tags) {
+ SYMRES(expression, meta);
+ }
+ }
+
+ bh_arr_each(AstStructMember *, smem, st->members) {
+ if ((*smem)->initial_value != NULL) {
+ SYMRES(expression, &(*smem)->initial_value);
+ }
+
+ if ((*smem)->meta_tags != NULL) {
+ bh_arr_each(AstTyped *, meta, (*smem)->meta_tags) {
+ SYMRES(expression, meta);
+ }
+ }
+ }
+
+ if (st->scope) scope_leave();
+ return Symres_Success;
+}
+
+static SymresStatus symres_polyproc(AstFunction* pp) {
+ pp->flags |= Ast_Flag_Comptime;
+ pp->parent_scope_of_poly_proc = curr_scope;
+ return Symres_Success;
+}
+
+static SymresStatus symres_static_if(AstIf* static_if) {
+ if (static_if->flags & Ast_Flag_Dead) return Symres_Complete;
+
+ SYMRES(expression, &static_if->cond);
+ return Symres_Success;
+}
+
+static SymresStatus symres_process_directive(AstNode* directive) {
+ // :EliminatingSymres
+ 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, Error_Critical, "#match directive expects a matched procedure.");
+ return Symres_Error;
+ }
+
+ AstOverloadedFunction* ofunc = (AstOverloadedFunction *) add_overload->overloaded_function;
+ if (ofunc->locked) {
+ onyx_report_error(add_overload->token->pos, Error_Critical, "Cannot add match option here as the original #match was declared as #locked.");
+ onyx_report_error(ofunc->token->pos, Error_Critical, "Here is the original #match.");
+ return Symres_Error;
+ }
+
+ if (ofunc->only_local_functions) {
+ if (!token_same_file(add_overload->token, ofunc->token)) {
+ onyx_report_error(add_overload->token->pos, Error_Critical, "Cannot add match option here as this option is not within the same file as the original #match declared with #local.");
+ onyx_report_error(ofunc->token->pos, Error_Critical, "Here is the original #match.");
+ }
+ }
+
+ 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, Error_Critical, "This cannot be used as an operator overload.");
+ return Symres_Error;
+ }
+
+ if (operator->operator != Binary_Op_Subscript_Equals && bh_arr_length(overload->params) != 2) {
+ onyx_report_error(operator->token->pos, Error_Critical, "Expected exactly 2 arguments for binary operator overload.");
+ 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);
+ SYMRES(expression, &export->export_name_expr);
+
+ if (export->export->kind == Ast_Kind_Polymorphic_Proc) {
+ onyx_report_error(export->token->pos, Error_Critical, "Cannot export a polymorphic function.");
+ return Symres_Error;
+ }
+
+ if (export->export->kind == Ast_Kind_Function) {
+ AstFunction *func = (AstFunction *) export->export;
+ func->exported_name = export->export_name;
+ func->is_exported = 1;
+
+ if (func->is_exported) {
+ if (func->is_foreign) {
+ onyx_report_error(export->token->pos, Error_Critical, "Cannot export a foreign function.");
+ return Symres_Error;
+ }
+
+ if (func->is_intrinsic) {
+ onyx_report_error(export->token->pos, Error_Critical, "Cannot export an intrinsic function.");
+ return Symres_Error;
+ }
+ }
+ }
+
+ break;
+ }
+
+ case Ast_Kind_Directive_Init: {
+ AstDirectiveInit *init = (AstDirectiveInit *) directive;
+ SYMRES(expression, &init->init_proc);
+
+ if (init->dependencies) {
+ bh_arr_each(AstDirectiveInit *, dependency, init->dependencies) {
+ SYMRES(expression, (AstTyped **) dependency);
+ }
+ }
+
+ break;
+ }
+
+ case Ast_Kind_Directive_Library: {
+ AstDirectiveLibrary *library = (AstDirectiveLibrary *) directive;
+ SYMRES(expression, &library->library_symbol);
+ break;
+ }
+
+ case Ast_Kind_Injection: {
+ AstInjection *inject = (AstInjection *) directive;
+
+ if (inject->dest == NULL) {
+ if (inject->full_loc == NULL) return Symres_Error;
+
+ if (inject->full_loc->kind != Ast_Kind_Field_Access) {
+ onyx_report_error(inject->token->pos, Error_Critical, "#inject expects a dot (a.b) expression for the injection point.");
+ return Symres_Error;
+ }
+
+ AstFieldAccess *acc = (AstFieldAccess *) inject->full_loc;
+ inject->dest = acc->expr;
+ inject->symbol = acc->token;
+ }
+
+ SYMRES(expression, &inject->dest);
+ SYMRES(expression, &inject->to_inject);
+
+ Scope *scope = get_scope_from_node_or_create((AstNode *) inject->dest);
+ if (scope == NULL) {
+ onyx_report_error(inject->token->pos, Error_Critical, "Cannot #inject here.");
+ return Symres_Error;
+ }
+
+ AstBinding *binding = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinding), Ast_Kind_Binding);
+ binding->token = inject->symbol;
+ binding->node = (AstNode *) inject->to_inject;
+
+ Package *pac = NULL;
+ if (inject->dest->kind == Ast_Kind_Package) {
+ pac = ((AstPackage *) inject->dest)->package;
+ }
+
+ add_entities_for_node(NULL, (AstNode *) binding, scope, pac);
+ return Symres_Complete;
+ }
+ }
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_macro(AstMacro* macro) {
+ macro->flags |= Ast_Flag_Comptime;
+
+ if (macro->body->kind == Ast_Kind_Function) {
+ SYMRES(function_header, (AstFunction *) macro->body);
+ }
+ else if (macro->body->kind == Ast_Kind_Polymorphic_Proc) {
+ SYMRES(polyproc, (AstFunction *) macro->body);
+ }
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_constraint(AstConstraint* constraint) {
+ switch (constraint->phase) {
+ case Constraint_Phase_Cloning_Expressions:
+ case Constraint_Phase_Waiting_To_Be_Queued: {
+ SYMRES(expression, (AstTyped **) &constraint->interface);
+
+ bh_arr_each(AstType *, type_arg, constraint->type_args) {
+ SYMRES(type, type_arg);
+ }
+
+ return Symres_Success;
+ }
+
+ case Constraint_Phase_Checking_Expressions: {
+ fori (i, constraint->expr_idx, bh_arr_length(constraint->exprs)) {
+ SYMRES(expression, &constraint->exprs[i].expr);
+
+ if (constraint->exprs[i].expected_type_expr) {
+ SYMRES(type, &constraint->exprs[i].expected_type_expr);
+ }
+ }
+
+ return Symres_Success;
+ }
+ }
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_polyquery(AstPolyQuery *query) {
+ // :EliminatingSymres
+ query->successful_symres = 0;
+
+ if (query->function_header->scope == NULL)
+ query->function_header->scope = scope_create(context.ast_alloc, query->proc->parent_scope_of_poly_proc, query->token->pos);
+
+ scope_enter(query->function_header->scope);
+
+ u32 idx = 0;
+ bh_arr_each(AstParam, param, query->function_header->params) {
+ bh_arr_each(AstPolyParam, pp, query->proc->poly_params) {
+ if (pp->kind == PPK_Baked_Value && pp->idx == idx) goto skip_introducing_symbol;
+ }
+
+ symbol_introduce(curr_scope, param->local->token, (AstNode *) param->local);
+
+ skip_introducing_symbol:
+ idx++;
+ }
+
+ bh_arr_each(AstParam, param, query->function_header->params) {
+ if (param->local->type_node != NULL) {
+ resolved_a_symbol = 0;
+
+ param->local->flags |= Ast_Flag_Symbol_Invisible;
+ symres_type(¶m->local->type_node);
+ param->local->flags &= ~Ast_Flag_Symbol_Invisible;
+
+ onyx_clear_errors();
+
+ if (resolved_a_symbol) query->successful_symres = 1;
+ }
+ }
+
+ scope_leave();
+ return Symres_Success;
+}
+
+static SymresStatus symres_foreign_block(AstForeignBlock *fb) {
+ if (fb->scope == NULL)
+ fb->scope = scope_create(context.ast_alloc, curr_scope, fb->token->pos);
+
+ bh_arr_each(Entity *, pent, fb->captured_entities) {
+ Entity *ent = *pent;
+ if (ent->type == Entity_Type_Function_Header) {
+ if (ent->function->body->next != NULL) {
+ onyx_report_error(ent->function->token->pos, Error_Critical, "Procedures declared in a #foreign block should not have bodies.");
+ return Symres_Error;
+ }
+
+ ent->function->foreign_name = ent->function->intrinsic_name; // Hmm... This might not be right?
+ ent->function->foreign_module = fb->module_name;
+ ent->function->is_foreign = 1;
+ ent->function->entity = NULL;
+ ent->function->entity_header = NULL;
+ ent->function->entity_body = NULL;
+
+ add_entities_for_node(NULL, (AstNode *) ent->function, ent->scope, ent->package);
+ continue;
+ }
+
+ if (ent->type == Entity_Type_Binding) {
+ AstBinding* new_binding = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinding), Ast_Kind_Binding);
+ new_binding->token = ent->binding->token;
+ new_binding->node = ent->binding->node;
+
+ Entity e;
+ memset(&e, 0, sizeof(e));
+ e.type = Entity_Type_Binding;
+ e.state = Entity_State_Introduce_Symbols;
+ e.binding = new_binding;
+ e.scope = fb->scope;
+ e.package = ent->package;
+
+ entity_heap_insert(&context.entities, e);
+ }
+
+ if (ent->type != Entity_Type_Function) {
+ entity_heap_insert_existing(&context.entities, ent);
+ }
+ }
+
+ return Symres_Complete;
+}
+
+static SymresStatus symres_include(AstInclude* include) {
+ if (include->name != NULL) return Symres_Goto_Parse;
+
+ SYMRES(expression, &include->name_node);
+
+ if (include->name_node->kind != Ast_Kind_StrLit) {
+ onyx_report_error(include->token->pos, Error_Critical, "Expected compile-time known string literal here. Got '%s'.", onyx_ast_node_kind_string(include->name_node->kind));
+ return Symres_Error;
+ }
+
+ OnyxToken* str_token = include->name_node->token;
+ if (str_token != NULL) {
+ token_toggle_end(str_token);
+ include->name = bh_strdup(context.ast_alloc, str_token->text);
+ string_process_escape_seqs(include->name, include->name, strlen(include->name));
+ token_toggle_end(str_token);
+ }
+
+ return Symres_Goto_Parse;
+}
+
+static SymresStatus symres_file_contents(AstFileContents* fc) {
+ SYMRES(expression, &fc->filename_expr);
+
+ if (fc->filename_expr->kind != Ast_Kind_StrLit) {
+ onyx_report_error(fc->token->pos, Error_Critical, "Expected given expression to be a compile-time stirng literal.");
+ return Symres_Error;
+ }
+
+ return Symres_Success;
+}
+
+void symres_entity(Entity* ent) {
+ if (ent->scope) scope_enter(ent->scope);
+
+ report_unresolved_symbols = 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_Load_Path:
+ case Entity_Type_Load_File: ss = symres_include(ent->include); break;
+ case Entity_Type_File_Contents: ss = symres_file_contents(ent->file_contents); break;
+
+ case Entity_Type_Foreign_Function_Header:
+ case Entity_Type_Temp_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_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_Polymorphic_Proc: ss = symres_polyproc(ent->poly_proc);
+ 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_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;
+ case Entity_Type_Constraint_Check: ss = symres_constraint(ent->constraint); break;
+ case Entity_Type_Polymorph_Query: ss = symres_polyquery(ent->poly_query); break;
+ case Entity_Type_Foreign_Block: ss = symres_foreign_block(ent->foreign_block);
+ if (context.options->generate_foreign_info) {
+ next_state = Entity_State_Check_Types;
+ ss = Symres_Success;
+ }
+ break;
+
+ default: break;
+ }
+
+ if (ss == Symres_Yield_Macro) ent->macro_attempts++;
+ if (ss == Symres_Yield_Micro) ent->micro_attempts++;
+ if (ss == Symres_Complete) ent->state = Entity_State_Finalized;
+ if (ss == Symres_Goto_Parse) ent->state = Entity_State_Parse;
+ if (ss == Symres_Success) {
+ ent->macro_attempts = 0;
+ ent->micro_attempts = 0;
+ ent->state = next_state;
+ }
+
+ curr_scope = NULL;
+}
--- /dev/null
+#define BH_DEBUG
+#include "stb_ds.h"
+#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, POINTER_SIZE, POINTER_SIZE, "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_array_map;
+static bh_imap type_slice_map;
+static bh_imap type_dynarr_map;
+static bh_imap type_vararg_map;
+static 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_array_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);
+ sh_new_arena(type_func_map);
+
+ 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;
+
+ bh_arr(StructMember *) members = t1_struct->Struct.memarr;
+ if (bh_arr_length(members) > 0 && members[0]->used)
+ return types_are_compatible(t2_struct,members[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.elem, t2->Slice.elem);
+ }
+
+ case Type_Kind_VarArgs: {
+ if (t2->kind != Type_Kind_VarArgs) return 0;
+ return types_are_compatible(t1->VarArgs.elem, t2->VarArgs.elem);
+ }
+
+ case Type_Kind_DynArray: {
+ if (t2->kind != Type_Kind_DynArray) return 0;
+ return types_are_compatible(t1->DynArray.elem, t2->DynArray.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;
+ }
+
+ case Type_Kind_Distinct:
+ // If the above cases didn't catch it, then these distinct types are not compatible.
+ return 0;
+
+ 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 POINTER_SIZE;
+ 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 POINTER_SIZE * 2; // HACK: These should not have to be 16 bytes in size, they should only have to be 12,
+ case Type_Kind_VarArgs: return POINTER_SIZE * 2; // 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 POINTER_SIZE + 8 + 2 * POINTER_SIZE; // data (8), count (4), capacity (4), allocator { func (4), ---(4), data (8) }
+ case Type_Kind_Compound: return type->Compound.size;
+ case Type_Kind_Distinct: return type_size_of(type->Distinct.base_type);
+ 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 POINTER_SIZE;
+ 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 POINTER_SIZE;
+ case Type_Kind_VarArgs: return POINTER_SIZE;
+ case Type_Kind_DynArray: return POINTER_SIZE;
+ case Type_Kind_Compound: return 4; // HACK
+ case Type_Kind_Distinct: return type_alignment_of(type->Distinct.base_type);
+ default: return 1;
+ }
+}
+
+// If this function returns NULL, then the caller MUST yield because the type may still be constructed in the future.
+// If there was an error constructing the type, then this function will report that directly.
+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: {
+ // ((AstPointerType *) type_node)->elem->flags |= type_node->flags & Ast_Flag_Header_Check_No_Error;
+ 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 (func_type->Function.return_type != &type_auto_return) {
+ i32 index = shgeti(type_func_map, name);
+ if (index != -1) {
+ u64 id = type_func_map[index].value;
+ 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);
+ shput(type_func_map, name, func_type->id);
+
+ return func_type;
+ }
+
+ case Ast_Kind_Array_Type: {
+ AstArrayType* a_node = (AstArrayType *) type_node;
+
+ Type *elem_type = type_build_from_ast(alloc, a_node->elem);
+ if (elem_type == NULL) return NULL;
+
+ 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(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, Error_Critical, "Array type expects type 'i32' for size, got '%s'.",
+ type_get_name(a_node->count_expr->type));
+ return NULL;
+ }
+
+ count = get_expression_integer_value(a_node->count_expr, NULL);
+ }
+
+ Type* array_type = type_make_array(alloc, elem_type, count);
+ if (array_type) array_type->ast_type = type_node;
+ return array_type;
+ }
+
+ case Ast_Kind_Struct_Type: {
+ AstStructType* s_node = (AstStructType *) type_node;
+ if (s_node->stcache != NULL) return s_node->stcache;
+ if (s_node->pending_type != NULL && s_node->pending_type_is_valid) return s_node->pending_type;
+ if (!s_node->ready_to_build_type) return NULL;
+
+ Type* s_type;
+ if (s_node->pending_type == NULL) {
+ s_type = type_create(Type_Kind_Struct, alloc, 0);
+ s_node->pending_type = 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);
+ s_type->Struct.meta_tags = s_node->meta_tags;
+ s_type->Struct.constructed_from = NULL;
+ s_type->Struct.status = SPS_Start;
+ type_register(s_type);
+
+ s_type->Struct.memarr = NULL;
+ sh_new_arena(s_type->Struct.members);
+ bh_arr_new(global_heap_allocator, s_type->Struct.memarr, s_type->Struct.mem_count);
+
+ } else {
+ s_type = s_node->pending_type;
+ }
+
+ s_type->Struct.poly_sln = NULL;
+
+ bh_arr_clear(s_type->Struct.memarr);
+ shfree(s_type->Struct.members);
+ sh_new_arena(s_type->Struct.members);
+
+ s_node->pending_type_is_valid = 1;
+
+ b32 is_union = s_node->is_union;
+ 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->pending_type_is_valid = 0;
+ return NULL;
+ }
+
+ mem_alignment = type_alignment_of((*member)->type);
+ if (mem_alignment <= 0) {
+ onyx_report_error((*member)->token->pos, Error_Critical, "Invalid member type: %s. Has alignment %d", type_get_name((*member)->type), mem_alignment);
+ return NULL;
+ }
+
+ if (mem_alignment > alignment) alignment = mem_alignment;
+
+ if (!s_node->is_packed) {
+ bh_align(offset, mem_alignment);
+ }
+
+ token_toggle_end((*member)->token);
+ if (shgeti(s_type->Struct.members, (*member)->token->text) != -1) {
+ onyx_report_error((*member)->token->pos, Error_Critical, "Duplicate struct member, '%s'.", (*member)->token->text);
+ return NULL;
+ }
+
+ StructMember* smem = bh_alloc_item(alloc, StructMember);
+ smem->offset = offset;
+ smem->type = (*member)->type;
+ smem->idx = idx;
+ smem->name = bh_strdup(alloc, (*member)->token->text);
+ smem->token = (*member)->token;
+ smem->initial_value = &(*member)->initial_value;
+ smem->meta_tags = (*member)->meta_tags;
+
+ smem->included_through_use = 0;
+ smem->used = (*member)->is_used;
+ smem->use_through_pointer_index = -1;
+ shput(s_type->Struct.members, (*member)->token->text, smem);
+ bh_arr_push(s_type->Struct.memarr, smem);
+ token_toggle_end((*member)->token);
+
+ u32 type_size = type_size_of((*member)->type);
+
+ if (!is_union) {
+ offset += type_size;
+ size = offset;
+ } else {
+ size = bh_max(size, type_size);
+ }
+
+ idx++;
+ }
+
+ u32 min_alignment = get_expression_integer_value(s_node->min_alignment_, NULL);
+ alignment = bh_max(min_alignment, alignment);
+ if (!s_node->is_packed) {
+ bh_align(size, alignment);
+ }
+
+ u32 min_size = get_expression_integer_value(s_node->min_size_, NULL);
+ size = bh_max(min_size, size);
+
+ s_type->Struct.alignment = alignment;
+ 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);
+
+ s_type->Struct.status = SPS_Members_Done;
+ 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->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: {
+ if (type_node->type_id != 0) return NULL;
+
+ Type* p_type = type_create(Type_Kind_PolyStruct, alloc, 0);
+ p_type->ast_type = type_node;
+ p_type->PolyStruct.name = ((AstPolyStructType *) type_node)->name;
+ p_type->PolyStruct.meta_tags = ((AstPolyStructType *) type_node)->base_struct->meta_tags;
+
+ type_register(p_type);
+ return NULL;
+ }
+
+ 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, Error_Critical, "Cannot instantiate a concrete type off of a non-polymorphic type.");
+ onyx_report_error(pc_type->callee->token->pos, Error_Critical, "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,
+ }));
+ }
+ }
+
+ Type* concrete = polymorphic_struct_lookup(ps_type, slns, pc_type->token->pos, (pc_type->flags & Ast_Flag_Header_Check_No_Error) == 0);
+
+ // 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;
+ if (concrete == (Type *) &node_that_signals_failure) return concrete;
+ concrete->Struct.constructed_from = (AstType *) ps_type;
+ return concrete;
+ }
+
+ 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;
+ }
+
+ case Ast_Kind_Distinct_Type: {
+ AstDistinctType* distinct = (AstDistinctType *) type_node;
+ if (distinct->dtcache) return distinct->dtcache;
+
+ Type *base_type = type_build_from_ast(alloc, distinct->base_type);
+ if (base_type == NULL) return NULL;
+ if (base_type->kind != Type_Kind_Basic) {
+ onyx_report_error(distinct->token->pos, Error_Critical, "Distinct types can only be made out of primitive types. '%s' is not a primitive type.", type_get_name(base_type));
+ return NULL;
+ }
+
+ Type *distinct_type = type_create(Type_Kind_Distinct, alloc, 0);
+ distinct_type->Distinct.base_type = base_type;
+ distinct_type->Distinct.name = distinct->name;
+ distinct->dtcache = distinct_type;
+
+ type_register(distinct_type);
+ return distinct_type;
+ }
+ }
+
+ 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 (func_type->Function.return_type != &type_auto_return) {
+ i32 index = shgeti(type_func_map, name);
+ if (index != -1) {
+ u64 id = type_func_map[index].value;
+ 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);
+ shput(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;
+ if (compound->exprs[i]->type->kind == Type_Kind_Basic) {
+ if (compound->exprs[i]->type->Basic.kind == Basic_Kind_Int_Unsized || compound->exprs[i]->type->Basic.kind == Basic_Kind_Float_Unsized) {
+ 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_build_implicit_type_of_struct_literal(bh_allocator alloc, AstStructLiteral* lit) {
+ Type* type = type_create(Type_Kind_Struct, alloc, 0);
+ type->ast_type = NULL;
+ type->Struct.name = NULL;
+ type->Struct.mem_count = bh_arr_length(lit->args.named_values);
+ type->Struct.meta_tags = NULL;
+ type->Struct.constructed_from = NULL;
+ type->Struct.status = SPS_Start;
+ type->Struct.poly_sln = NULL;
+ type_register(type);
+
+ type->Struct.memarr = NULL;
+ sh_new_arena(type->Struct.members);
+ bh_arr_new(global_heap_allocator, type->Struct.memarr, type->Struct.mem_count);
+
+ u32 size = 0;
+ u32 offset = 0;
+ u32 alignment = 1;
+ u32 idx = 0;
+ bh_arr_each(AstNamedValue *, pnv, lit->args.named_values) {
+ AstNamedValue *nv = *pnv;
+
+ Type* member_type = resolve_expression_type(nv->value);
+ if (member_type == NULL) {
+ return NULL;
+ }
+
+ u32 mem_alignment = type_alignment_of(member_type);
+ if (mem_alignment <= 0) {
+ return NULL;
+ }
+
+ alignment = bh_max(alignment, mem_alignment);
+
+ // Should these structs be packed or not?
+ bh_align(offset, mem_alignment);
+
+ token_toggle_end(nv->token);
+ if (shgeti(type->Struct.members, nv->token->text) != -1) {
+ token_toggle_end(nv->token);
+ return NULL;
+ }
+
+ StructMember *smem = bh_alloc_item(alloc, StructMember);
+ smem->offset = offset;
+ smem->type = member_type;
+ smem->idx = idx;
+ smem->name = bh_strdup(alloc, nv->token->text);
+ smem->token = nv->token;
+ smem->initial_value = &nv->value;
+ smem->meta_tags = NULL;
+ smem->included_through_use = 0;
+ smem->used = 0;
+ smem->use_through_pointer_index = -1;
+ shput(type->Struct.members, nv->token->text, smem);
+ bh_arr_push(type->Struct.memarr, smem);
+ token_toggle_end(nv->token);
+
+ u32 type_size = type_size_of(member_type);
+ offset += type_size;
+ size = offset;
+ idx++;
+ }
+
+ type->Struct.alignment = alignment;
+ type->Struct.size = size;
+ type->Struct.linear_members = NULL;
+ bh_arr_new(global_heap_allocator, type->Struct.linear_members, type->Struct.mem_count);
+ build_linear_types_with_offset(type, &type->Struct.linear_members, 0);
+
+ type->Struct.status = SPS_Uses_Done;
+ return type;
+}
+
+Type* type_make_pointer(bh_allocator alloc, Type* to) {
+ if (to == NULL) return NULL;
+ if (to == (Type *) &node_that_signals_failure) return to;
+
+ 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 = POINTER_SIZE;
+ 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;
+ if (to == (Type *) &node_that_signals_failure) return to;
+
+ assert(to->id > 0);
+ u64 key = ((((u64) to->id) << 32) | (u64) count);
+ u64 array_id = bh_imap_get(&type_array_map, key);
+ if (array_id > 0) {
+ Type* array_type = (Type *) bh_imap_get(&type_map, array_id);
+ return array_type;
+
+ } else {
+ 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);
+
+ type_register(arr_type);
+ bh_imap_put(&type_array_map, key, arr_type->id);
+
+ return arr_type;
+ }
+}
+
+Type* type_make_slice(bh_allocator alloc, Type* of) {
+ if (of == NULL) return NULL;
+ if (of == (Type *) &node_that_signals_failure) return of;
+
+ 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);
+
+ type_make_pointer(alloc, of);
+ slice_type->Slice.elem = of;
+
+ return slice_type;
+ }
+}
+
+Type* type_make_dynarray(bh_allocator alloc, Type* of) {
+ if (of == NULL) return NULL;
+ if (of == (Type *) &node_that_signals_failure) return of;
+
+ 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);
+
+ type_make_pointer(alloc, of);
+ dynarr->DynArray.elem = of;
+
+ return dynarr;
+ }
+}
+
+Type* type_make_varargs(bh_allocator alloc, Type* of) {
+ if (of == NULL) return NULL;
+ if (of == (Type *) &node_that_signals_failure) return of;
+
+ 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);
+
+ type_make_pointer(alloc, of);
+ va_type->VarArgs.elem = 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;
+ }
+}
+
+b32 type_struct_member_apply_use(bh_allocator alloc, Type *s_type, StructMember *smem) {
+ Type* used_type = smem->type;
+
+ b32 type_is_pointer = 0;
+ if (used_type->kind == Type_Kind_Pointer) {
+ type_is_pointer = 1;
+ used_type = type_get_contained_type(used_type);
+ }
+
+ if (used_type->kind != Type_Kind_Struct) {
+ onyx_report_error(smem->token->pos, Error_Critical, "Can only use things of structure, or pointer to structure type.");
+ return 0;
+ }
+
+ if (used_type->Struct.status == SPS_Start) return 0;
+
+ bh_arr_each(StructMember*, psmem, used_type->Struct.memarr) {
+ if (shgeti(s_type->Struct.members, (*psmem)->name) != -1) {
+ onyx_report_error(smem->token->pos, Error_Critical, "Used name '%s' conflicts with existing struct member.", (*psmem)->name);
+ return 0;
+ }
+
+ StructMember* new_smem = bh_alloc_item(alloc, StructMember);
+ new_smem->type = (*psmem)->type;
+ new_smem->name = (*psmem)->name;
+ new_smem->meta_tags = (*psmem)->meta_tags;
+ new_smem->used = 0;
+ new_smem->included_through_use = 1;
+
+ if (type_is_pointer) {
+ new_smem->offset = (*psmem)->offset;
+ new_smem->idx = (*psmem)->idx;
+ new_smem->initial_value = NULL;
+ new_smem->use_through_pointer_index = smem->idx;
+ } else {
+ new_smem->offset = smem->offset + (*psmem)->offset;
+ new_smem->idx = -1; // Dummy value because I don't think this is needed.
+ new_smem->initial_value = (*psmem)->initial_value;
+ new_smem->use_through_pointer_index = -1;
+ }
+
+ shput(s_type->Struct.members, (*psmem)->name, new_smem);
+ }
+
+ return 1;
+}
+
+const char* type_get_unique_name(Type* type) {
+ if (type == NULL) return "unknown";
+
+ switch (type->kind) {
+ case Type_Kind_Basic: return type->Basic.name;
+ case Type_Kind_Pointer: return bh_aprintf(global_scratch_allocator, "^%s", type_get_unique_name(type->Pointer.elem));
+ case Type_Kind_Array: return bh_aprintf(global_scratch_allocator, "[%d] %s", type->Array.count, type_get_unique_name(type->Array.elem));
+ case Type_Kind_Struct:
+ if (type->Struct.name)
+ return bh_aprintf(global_scratch_allocator, "%s@%l", type->Struct.name, type->id);
+ else
+ return bh_aprintf(global_scratch_allocator, "%s@%l", "<anonymous struct>", type->id);
+ case Type_Kind_Enum:
+ if (type->Enum.name)
+ return bh_aprintf(global_scratch_allocator, "%s@%l", type->Enum.name, type->id);
+ else
+ return bh_aprintf(global_scratch_allocator, "%s@%l", "<anonymous enum>", type->id);
+
+ case Type_Kind_Slice: return bh_aprintf(global_scratch_allocator, "[] %s", type_get_unique_name(type->Slice.elem));
+ case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_unique_name(type->VarArgs.elem));
+ case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_unique_name(type->DynArray.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);
+ }
+
+ case Type_Kind_Distinct: {
+ return bh_aprintf(global_scratch_allocator, "%s@%l", type->Distinct.name, type->id);
+ }
+
+ default: return "unknown (not null)";
+ }
+}
+
+const char* type_get_name(Type* type) {
+ if (type == NULL) return "unknown";
+
+ switch (type->kind) {
+ case Type_Kind_Basic: return type->Basic.name;
+ case Type_Kind_Pointer: return bh_aprintf(global_scratch_allocator, "^%s", type_get_name(type->Pointer.elem));
+ case Type_Kind_Array: return bh_aprintf(global_scratch_allocator, "[%d] %s", type->Array.count, type_get_name(type->Array.elem));
+ case Type_Kind_Struct:
+ if (type->Struct.name)
+ return type->Struct.name;
+ else
+ return "<anonymous struct>";
+ case Type_Kind_Enum:
+ if (type->Enum.name)
+ return type->Enum.name;
+ else
+ return "<anonymous enum>";
+
+ case Type_Kind_Slice: return bh_aprintf(global_scratch_allocator, "[] %s", type_get_name(type->Slice.elem));
+ case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_name(type->VarArgs.elem));
+ case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_name(type->DynArray.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);
+ }
+
+ case Type_Kind_Distinct: {
+ return bh_aprintf(global_scratch_allocator, "%s", type->Distinct.name);
+ }
+
+ 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;
+}
+
+Type* type_get_contained_type(Type* type) {
+ if (type == NULL) return NULL;
+ switch (type->kind) {
+ case Type_Kind_Pointer: return type->Pointer.elem;
+ case Type_Kind_Array: return type->Array.elem;
+ case Type_Kind_Slice: return type->Slice.elem;
+ case Type_Kind_DynArray: return type->DynArray.elem;
+ case Type_Kind_VarArgs: return type->VarArgs.elem;
+ default: return NULL;
+ }
+}
+
+static const StructMember slice_members[] = {
+ { 0, 0, NULL, "data", NULL, NULL, -1, 0, 0 },
+ { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "count", NULL, NULL, -1, 0, 0 },
+ { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "size", NULL, NULL, -1, 0, 0 },
+ { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "length", NULL, NULL, -1, 0, 0 },
+};
+
+static const StructMember array_members[] = {
+ { 0, 0, NULL, "data", NULL, NULL, -1, 0, 0 },
+ { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "count", NULL, NULL, -1, 0, 0 },
+ { POINTER_SIZE + 4, 2, &basic_types[Basic_Kind_U32], "capacity", NULL, NULL, -1, 0, 0 },
+ { POINTER_SIZE + 8, 3, NULL, "allocator", NULL, NULL, -1, 0, 0 },
+ { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "size", NULL, NULL, -1, 0, 0 },
+ { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "length", NULL, NULL, -1, 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;
+
+ i32 index = shgeti(stype->members, member);
+ if (index == -1) return 0;
+ *smem = *stype->members[index].value;
+ 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_make_pointer(context.ast_alloc, type->Slice.elem);
+
+ 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_make_pointer(context.ast_alloc, type->DynArray.elem);
+ 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_make_pointer(context.ast_alloc, type->Slice.elem);
+
+ return 1;
+ }
+
+ case Type_Kind_DynArray: {
+ if (idx > 4) return 0;
+
+ *smem = array_members[idx];
+ if (idx == 0) smem->type = type_make_pointer(context.ast_alloc, type->DynArray.elem);
+ 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_make_pointer(context.ast_alloc, type->Slice.elem);
+ two->offset = 0;
+ }
+ if (idx == 1) {
+ two->type = &basic_types[Basic_Kind_U32];
+ two->offset = POINTER_SIZE;
+ }
+
+ return 1;
+ }
+ case Type_Kind_DynArray: {
+ if (idx == 0) {
+ two->type = type_make_pointer(context.ast_alloc, type->DynArray.elem);
+ two->offset = 0;
+ }
+ if (idx == 1) {
+ two->type = &basic_types[Basic_Kind_U32];
+ two->offset = POINTER_SIZE;
+ }
+ if (idx == 2) {
+ two->type = &basic_types[Basic_Kind_U32];
+ two->offset = POINTER_SIZE + 4;
+ }
+ 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 += POINTER_SIZE + 8;
+ }
+
+ 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;
+
+ case Type_Kind_Distinct:
+ two->type = type->Distinct.base_type;
+ two->offset = 0;
+ 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 == POINTER_SIZE) return 1;
+ return -1;
+ }
+ case Type_Kind_DynArray: {
+ if (offset == 0) return 0;
+ if (offset == POINTER_SIZE) return 1;
+ if (offset == POINTER_SIZE + 4) return 2;
+ if (offset == POINTER_SIZE + 8) return 3;
+ if (offset == POINTER_SIZE * 2 + 8) 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) || (*mem)->type->kind == Type_Kind_Array) {
+ 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_Enum) return type_is_small_integer(type->Enum.backing);
+ if (type->kind == Type_Kind_Distinct) return type_is_small_integer(type->Distinct.base_type);
+ 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_Enum) return type_is_integer(type->Enum.backing);
+ if (type->kind == Type_Kind_Distinct) return type_is_integer(type->Distinct.base_type);
+ 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_Distinct) return type_is_numeric(type->Distinct.base_type);
+ 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;
+
+ if (type->kind == Type_Kind_Struct) {
+ //
+ // This is for the kind of common case where a structure simply wraps a
+ // single non-compound value; in this situation, the structure can be
+ // "dissolved" at compile-time and turn into the underlying type.
+ //
+
+ if (bh_arr_length(type->Struct.linear_members) != 1) return 1;
+ return type_is_compound(type->Struct.linear_members[0].type);
+ }
+
+ 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
+ && type->kind != Type_Kind_Distinct;
+}
+
+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_Array) return 1;
+ if (type->kind == Type_Kind_Struct) return 1;
+ if (type->kind == Type_Kind_Slice) return 1;
+ if (type->kind == Type_Kind_DynArray) return 1;
+ if (type->kind == Type_Kind_VarArgs) return 1;
+ if (type->kind == Type_Kind_Pointer) {
+ if (type->Pointer.elem->kind == Type_Kind_Struct) return 1;
+ if (type->Pointer.elem->kind == Type_Kind_Slice) return 1;
+ if (type->Pointer.elem->kind == Type_Kind_DynArray) return 1;
+ }
+ return 0;
+}
+
+b32 type_is_structlike_strict(Type* type) {
+ if (type == NULL) return 0;
+ if (type->kind == Type_Kind_Struct) return 1;
+ if (type->kind == Type_Kind_Slice) return 1;
+ if (type->kind == Type_Kind_DynArray) return 1;
+ if (type->kind == Type_Kind_VarArgs) return 1;
+ return 0;
+}
+
+u32 type_structlike_mem_count(Type* type) {
+ if (type == NULL) return 0;
+ switch (type->kind) {
+ case Type_Kind_Struct: return type->Struct.mem_count;
+ case Type_Kind_Slice: return 2;
+ case Type_Kind_VarArgs: return 2;
+ case Type_Kind_DynArray: return 4;
+ default: return 0;
+ }
+}
+
+u32 type_structlike_is_simple(Type* type) {
+ if (type == NULL) return 0;
+ switch (type->kind) {
+ case Type_Kind_Struct: return type_struct_is_simple(type);
+ case Type_Kind_Slice: return 1;
+ case Type_Kind_VarArgs: return 1;
+ case Type_Kind_DynArray: return 0;
+ default: return 0;
+ }
+}
+
+b32 type_is_sl_constructable(Type* type) {
+ if (type == NULL) return 0;
+ switch (type->kind) {
+ case Type_Kind_Struct: return 1;
+ case Type_Kind_Slice: return 1;
+ case Type_Kind_DynArray: return 1;
+ default: return 0;
+ }
+}
+
+b32 type_struct_constructed_from_poly_struct(Type* struct_type, struct AstType* from) {
+ if (struct_type == NULL) return 0;
+ if (struct_type->kind != Type_Kind_Struct) return 0;
+
+ return struct_type->Struct.constructed_from == from;
+}
--- /dev/null
+#define BH_DEBUG
+
+#include "utils.h"
+#include "lex.h"
+#include "astnodes.h"
+#include "errors.h"
+#include "parser.h"
+#include "astnodes.h"
+#include "errors.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) {
+ i32 index = shgeti(context.packages, package_name);
+ if (index != -1) {
+ return context.packages[index].value;
+ } else {
+ return NULL;
+ }
+}
+
+Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc, OnyxFilePos pos) {
+ i32 index = shgeti(context.packages, package_name);
+ if (index != -1) {
+ return context.packages[index].value;
+
+ } 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->use_package_entities = NULL;
+
+ if (!strcmp(pac_name, "builtin")) {
+ package->private_scope = scope_create(alloc, context.global_scope, pos);
+ package->scope = context.global_scope;
+ } else {
+ package->scope = scope_create(alloc, parent_scope, pos);
+ package->private_scope = scope_create(alloc, package->scope, pos);
+ }
+
+ shput(context.packages, pac_name, package);
+
+ if (!charset_contains(pac_name, '.')) {
+ AstPackage* package_node = onyx_ast_node_new(alloc, sizeof(AstPackage), Ast_Kind_Package);
+ package_node->package_name = package->name;
+ package_node->package = package;
+
+ symbol_raw_introduce(context.global_scope, pac_name, pos, (AstNode *) package_node);
+ }
+
+ return package;
+ }
+}
+
+void package_track_use_package(Package* package, Entity* entity) {
+ assert(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) return;
+ 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->name = NULL;
+
+ scope->symbols = NULL;
+ sh_new_arena(scope->symbols);
+
+ return scope;
+}
+
+void scope_include(Scope* target, Scope* source, OnyxFilePos pos) {
+ fori (i, 0, shlen(source->symbols)) {
+ symbol_raw_introduce(target, source->symbols[i].key, pos, source->symbols[i].value);
+ }
+}
+
+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, "_")) {
+ i32 index = shgeti(scope->symbols, name);
+ if (index != -1) {
+ AstNode *node = scope->symbols[index].value;
+ if (node != symbol) {
+ onyx_report_error(pos, Error_Critical, "Redeclaration of symbol '%s'.", name);
+
+ if (node->token) {
+ onyx_report_error(node->token->pos, Error_Critical, "Previous declaration was here.");
+ }
+
+ return 0;
+ }
+ return 1;
+ }
+ }
+
+ shput(scope->symbols, name, symbol);
+ return 1;
+}
+
+void symbol_builtin_introduce(Scope* scope, char* sym, AstNode *node) {
+ shput(scope->symbols, sym, node);
+}
+
+void symbol_subpackage_introduce(Scope* scope, char* sym, AstPackage* package) {
+ i32 index = shgeti(scope->symbols, sym);
+ if (index != -1) {
+ AstNode* maybe_package = scope->symbols[index].value;
+
+ // CLEANUP: Make this assertion an actual error message.
+ assert(maybe_package->kind == Ast_Kind_Package);
+ } else {
+ shput(scope->symbols, sym, (AstNode *) package);
+ }
+}
+
+AstNode* symbol_raw_resolve(Scope* start_scope, char* sym) {
+ Scope* scope = start_scope;
+
+ while (scope != NULL) {
+ i32 index = shgeti(scope->symbols, sym);
+ if (index != -1) {
+ AstNode* res = scope->symbols[index].value;
+
+ if ((res->flags & Ast_Flag_Symbol_Invisible) == 0) {
+ return res;
+ }
+ }
+
+ scope = scope->parent;
+ }
+
+ return NULL;
+}
+
+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_Foreign_Block: {
+ AstForeignBlock* fb = (AstForeignBlock *) node;
+
+ if (fb->scope == NULL)
+ return NULL;
+
+ return symbol_raw_resolve(fb->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;
+
+ // HACK HACK
+ // Temporarily disable the parent scope so that you can't access things
+ // "above" the structures scope. This leads to unintended behavior, as when
+ // you are accessing a static element on a structure, you don't expect to
+ // bleed to the top level scope.
+ AstNode *result = NULL;
+ if (stype->scope) {
+ Scope *tmp_parent = stype->scope->parent;
+ stype->scope->parent = NULL;
+ result = symbol_raw_resolve(stype->scope, symbol);
+ stype->scope->parent = tmp_parent;
+ }
+
+ 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);
+ }
+
+ case Ast_Kind_Poly_Call_Type: {
+ AstNode* callee = (AstNode *) ((AstPolyCallType *) node)->callee;
+ return try_symbol_raw_resolve_from_node(callee, 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;
+}
+
+AstNode* try_symbol_raw_resolve_from_type(Type *type, char* symbol) {
+ while (type->kind == Type_Kind_Pointer) {
+ type = type->Pointer.elem;
+ }
+
+ if (type->kind == Type_Kind_Struct) {
+ if (type->Struct.poly_sln == NULL) return NULL;
+
+ bh_arr_each(AstPolySolution, sln, type->Struct.poly_sln) {
+ if (token_text_equals(sln->poly_sym->token, symbol)) {
+ if (sln->kind == PSK_Type) {
+ AstTypeRawAlias* alias = onyx_ast_node_new(context.ast_alloc, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
+ alias->type = &basic_types[Basic_Kind_Type_Index];
+ alias->to = sln->type;
+ return (AstNode *) alias;
+
+ } else {
+ return (AstNode *) sln->value;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void scope_clear(Scope* scope) {
+ sh_new_arena(scope->symbols);
+}
+
+// Polymorphic procedures are in their own file to clean up this file.
+#include "polymorph.h"
+
+//
+// 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) {
+ 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 *) strip_aliases((AstNode *) entry->key);
+ arguments_copy(&args, param_args);
+
+ AstFunction* overload = NULL;
+ switch (node->kind) {
+ case Ast_Kind_Macro: overload = macro_resolve_header((AstMacro *) node, param_args, NULL, 0); break;
+ case Ast_Kind_Polymorphic_Proc: overload = polymorphic_proc_build_only_header((AstFunction *) node, PPLM_By_Arguments, param_args); break;
+ case Ast_Kind_Function:
+ overload = (AstFunction *) node;
+ arguments_clear_baked_flags(&args);
+ break;
+ }
+
+ // NOTE: Overload is not something that is known to be overloadable.
+ if (overload == NULL) continue;
+ if (overload == (AstFunction *) &node_that_signals_a_yield) return (AstTyped *) overload;
+ 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.
+
+ // 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 (AstTyped *) &node_that_signals_a_yield;
+ }
+ assert(overload->type->kind == Type_Kind_Function);
+
+ arguments_remove_baked(&args);
+ arguments_ensure_length(&args, get_argument_buffer_size(&overload->type->Function, &args));
+
+ // NOTE: If the arguments cannot be placed successfully in the parameters list
+ if (!fill_in_arguments(&args, (AstNode *) overload, NULL, 0)) continue;
+
+ VarArgKind va_kind;
+ TypeMatch tm = check_arguments_against_type(&args, &overload->type->Function, &va_kind, NULL, NULL, NULL);
+ if (tm == TYPE_MATCH_SUCCESS) {
+ matched_overload = node;
+ break;
+ }
+
+ if (tm == TYPE_MATCH_YIELD) {
+ return (AstTyped *) &node_that_signals_a_yield;
+ }
+ }
+
+ 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;
+
+ TypeMatch tm = unify_node_and_type(&node, type);
+ if (tm == TYPE_MATCH_SUCCESS) {
+ matched_overload = node;
+ break;
+ }
+
+ if (tm == TYPE_MATCH_YIELD) {
+ return (AstTyped *) &node_that_signals_a_yield;
+ }
+ }
+
+ bh_imap_free(&all_overloads);
+ return matched_overload;
+}
+
+void report_unable_to_match_overload(AstCall* call, bh_arr(OverloadOption) overloads) {
+ 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, Error_Critical, "Unable to match overloaded function with provided argument types: (%s)", arg_str);
+
+ bh_free(global_scratch_allocator, arg_str);
+
+ // 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);
+
+ i32 i = 1;
+ bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
+ AstTyped* node = (AstTyped *) strip_aliases((AstNode *) entry->key);
+ onyx_report_error(node->token->pos, Error_Critical, "Here is one of the overloads. %d/%d", i++, bh_arr_length(all_overloads.entries));
+ }
+
+ bh_imap_free(&all_overloads);
+}
+
+
+//
+// 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.
+ Type *any_type = type_build_from_ast(context.ast_alloc, builtin_any_type);
+ fori (i, 0, bh_arr_length(call->args.values)) {
+ AstNode *value = (AstNode *) ((AstArgument *) call->args.values[i])->value;
+ assert(template->params[i].local->type);
+
+ Type *param_type = template->params[i].local->type;
+ if (param_type == any_type
+ || (param_type->kind == Type_Kind_VarArgs && param_type->VarArgs.elem == any_type)) {
+ onyx_report_error(macro->token->pos, Error_Critical, "Currently, macros do not support arguments of type 'any' or '..any'.");
+ }
+
+ symbol_introduce(argument_scope, template->params[i].local->token, value);
+ }
+
+ if (template->poly_scope != NULL)
+ scope_include(argument_scope, template->poly_scope, call->token->pos);
+
+ *(AstNode **) pcall = subst;
+ return;
+}
+
+AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite, b32 error_if_failed) {
+ switch (macro->body->kind) {
+ case Ast_Kind_Function: return (AstFunction *) macro->body;
+
+ case Ast_Kind_Polymorphic_Proc: {
+ AstFunction* pp = (AstFunction *) macro->body;
+ ensure_polyproc_cache_is_created(pp);
+
+ bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, PPLM_By_Arguments, args, callsite, error_if_failed);
+
+ if (slns == NULL) {
+ if (flag_to_yield) {
+ flag_to_yield = 0;
+ return (AstFunction *) &node_that_signals_a_yield;
+ }
+
+ return NULL;
+ }
+
+ return polymorphic_proc_build_only_header_with_slns(pp, slns, error_if_failed);
+ }
+
+ default: assert(("Bad macro body type.", 0));
+ }
+
+ return NULL;
+}
+
+
+//
+// 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;
+}
+
+static i32 non_baked_argument_count(Arguments* args) {
+ if (args->used_argument_count >= 0) return args->used_argument_count;
+
+ i32 count = 0;
+
+ bh_arr_each(AstTyped *, actual, args->values) {
+ if ((*actual)->kind != Ast_Kind_Argument) count++;
+ else if (!((AstArgument *) (*actual))->is_baked) count++;
+ }
+
+ bh_arr_each(AstNamedValue *, named_value, args->named_values) {
+ if ((*named_value)->value->kind != Ast_Kind_Argument) count++;
+ else if (!((AstArgument *) (*named_value)->value)->is_baked) count++;
+ }
+
+ args->used_argument_count = count;
+ return count;
+}
+
+i32 get_argument_buffer_size(TypeFunction* type, Arguments* args) {
+ i32 non_vararg_param_count = (i32) type->param_count;
+ if (non_vararg_param_count > 0) {
+ if (type->params[type->param_count - 1] == builtin_vararg_type_type) non_vararg_param_count--;
+ if (type->params[type->param_count - 1]->kind == Type_Kind_VarArgs) non_vararg_param_count--;
+ }
+
+ return bh_max(non_vararg_param_count, non_baked_argument_count(args));
+}
+
+// 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, b32 insert_zero_values) {
+
+ { // 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)) {
+ if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "Error placing value with name '%s' at index '%d'.", named_value->token->text, idx);
+ 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) {
+ if (insert_zero_values) {
+ assert(provider->token);
+ args->values[idx] = (AstTyped *) make_zero_value(context.ast_alloc, provider->token, NULL);
+ } else {
+ if (err_msg) *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) {
+ if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "Too many values provided. Expected at most %d.", maximum_arguments);
+ success = 0;
+ }
+
+ return success;
+}
+
+
+//
+// Argument checking
+//
+
+typedef enum ArgState {
+ AS_Expecting_Exact,
+ AS_Expecting_Typed_VA,
+ AS_Expecting_Untyped_VA,
+} ArgState;
+
+TypeMatch check_arguments_against_type(Arguments* args, TypeFunction* func_type, VarArgKind* va_kind,
+ OnyxToken* location, char* func_name, OnyxError* error) {
+ b32 permanent = location != NULL;
+ if (func_name == NULL) func_name = "UNKNOWN FUNCTION";
+
+ if (error) error->rank = Error_Critical;
+
+ bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) args->values;
+ i32 arg_count = get_argument_buffer_size(func_type, args);
+
+ Type **formal_params = func_type->params;
+ Type* variadic_type = NULL;
+ i64 any_type_id = type_build_from_ast(context.ast_alloc, builtin_any_type)->id;
+
+ ArgState arg_state = AS_Expecting_Exact;
+ u32 arg_pos = 0;
+ while (1) {
+ switch (arg_state) {
+ case AS_Expecting_Exact: {
+ if (arg_pos >= func_type->param_count) goto type_checking_done;
+
+ if (formal_params[arg_pos]->kind == Type_Kind_VarArgs) {
+ variadic_type = formal_params[arg_pos]->VarArgs.elem;
+ arg_state = AS_Expecting_Typed_VA;
+ continue;
+ }
+
+ if ((i16) arg_pos == func_type->vararg_arg_pos) {
+ arg_state = AS_Expecting_Untyped_VA;
+ continue;
+ }
+
+ if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
+
+ assert(arg_arr[arg_pos]->kind == Ast_Kind_Argument);
+ TypeMatch tm = unify_node_and_type_(&arg_arr[arg_pos]->value, formal_params[arg_pos], permanent);
+ if (tm == TYPE_MATCH_YIELD) return tm;
+ if (tm == TYPE_MATCH_FAILED) {
+ if (error != NULL) {
+ error->pos = arg_arr[arg_pos]->token->pos;
+ error->text = bh_aprintf(global_heap_allocator,
+ "The procedure '%s' expects a value of type '%s' for %d%s parameter, got '%s'.",
+ func_name,
+ 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));
+ }
+ return tm;
+ }
+
+ if (arg_arr[arg_pos]->value->type && arg_arr[arg_pos]->value->type->id != any_type_id && formal_params[arg_pos]->id == any_type_id) {
+ resolve_expression_type(arg_arr[arg_pos]->value);
+ if (error != NULL) {
+ arg_arr[arg_pos]->pass_as_any = 1;
+ }
+ }
+
+ arg_arr[arg_pos]->va_kind = VA_Kind_Not_VA;
+ break;
+ }
+
+ case AS_Expecting_Typed_VA: {
+ if (variadic_type->id == any_type_id) *va_kind = VA_Kind_Any;
+
+ if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
+
+ if (variadic_type->id == any_type_id) {
+ resolve_expression_type(arg_arr[arg_pos]->value);
+ if (arg_arr[arg_pos]->value->type == NULL) {
+ if (error != NULL) {
+ error->pos = arg_arr[arg_pos]->token->pos;
+ error->text = "Unable to resolve type of argument.";
+ }
+ return TYPE_MATCH_FAILED;
+ }
+
+ arg_arr[arg_pos]->va_kind = VA_Kind_Any;
+ break;
+ }
+
+ *va_kind = VA_Kind_Typed;
+
+ assert(arg_arr[arg_pos]->kind == Ast_Kind_Argument);
+ TypeMatch tm = unify_node_and_type_(&arg_arr[arg_pos]->value, variadic_type, permanent);
+ if (tm == TYPE_MATCH_YIELD) return tm;
+ if (tm == TYPE_MATCH_FAILED) {
+ if (error != NULL) {
+ error->pos = arg_arr[arg_pos]->token->pos,
+ error->text = bh_aprintf(global_heap_allocator,
+ "The procedure '%s' expects a value of type '%s' for the variadic parameter, got '%s'.",
+ func_name,
+ type_get_name(variadic_type),
+ node_get_type_name(arg_arr[arg_pos]->value));
+ }
+ return tm;
+ }
+
+ arg_arr[arg_pos]->va_kind = VA_Kind_Typed;
+ break;
+ }
+
+ case AS_Expecting_Untyped_VA: {
+ *va_kind = VA_Kind_Untyped;
+
+ if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
+
+ assert(arg_arr[arg_pos]->kind == Ast_Kind_Argument);
+ resolve_expression_type(arg_arr[arg_pos]->value);
+ if (arg_arr[arg_pos]->value->type == NULL) {
+ if (error != NULL) {
+ error->pos = arg_arr[arg_pos]->token->pos;
+ error->text = "Unable to resolve type for argument.";
+ }
+ return TYPE_MATCH_FAILED;
+ }
+
+ arg_arr[arg_pos]->va_kind = VA_Kind_Untyped;
+ break;
+ }
+ }
+
+ arg_pos++;
+ }
+
+type_checking_done:
+ if (arg_pos < func_type->needed_param_count) {
+ if (error != NULL) {
+ error->pos = location->pos;
+ error->text = "Too few arguments to function call.";
+ }
+ return TYPE_MATCH_FAILED;
+ }
+
+ if (arg_pos < (u32) arg_count) {
+ if (error != NULL) {
+ error->pos = location->pos;
+ error->text = bh_aprintf(global_heap_allocator, "Too many arguments to function call. %d %d", arg_pos, arg_count);
+ }
+ return TYPE_MATCH_FAILED;
+ }
+
+ return TYPE_MATCH_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;
+}
+
+static Scope **get_scope_from_node_helper(AstNode *node) {
+ 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;
+ if (package->package == NULL) return NULL;
+
+ return &package->package->scope;
+ }
+
+ case Ast_Kind_Enum_Type: {
+ AstEnumType* etype = (AstEnumType *) node;
+ return &etype->scope;
+ }
+
+ case Ast_Kind_Struct_Type: {
+ AstStructType* stype = (AstStructType *) node;
+ return &stype->scope;
+ }
+
+ case Ast_Kind_Poly_Struct_Type: {
+ AstPolyStructType* pstype = (AstPolyStructType *) node;
+ AstStructType* stype = pstype->base_struct;
+ return &stype->scope;
+ }
+ }
+
+ return NULL;
+}
+
+Scope *get_scope_from_node(AstNode *node) {
+ if (!node) return NULL;
+
+ Scope **pscope = get_scope_from_node_helper(node);
+ if (!pscope) return NULL;
+ return *pscope;
+}
+
+Scope *get_scope_from_node_or_create(AstNode *node) {
+ if (!node) return NULL;
+
+ Scope **pscope = get_scope_from_node_helper(node);
+ if (!pscope) return NULL;
+
+ // Create the scope if it does not exist.
+ // This uses a NULL parent, which I think is what
+ // is used in other parts of the compiler for struct/enum
+ // scopes?
+ if (!*pscope) {
+ assert(node->token);
+ *pscope = scope_create(context.ast_alloc, NULL, node->token->pos);
+ }
+
+ return *pscope;
+}
+
+u32 levenshtein_distance(const char *str1, const char *str2) {
+ i32 m = strlen(str1) + 1;
+ i32 n = strlen(str2) + 1;
+
+ i32 *d = bh_alloc_array(global_scratch_allocator, i32, m * n);
+ fori (i, 0, m * n) d[i] = 0;
+
+ fori (i, 0, m) d[i * n + 0] = i;
+ fori (j, 0, n) d[0 * n + j] = j;
+
+ fori (j, 1, n) {
+ fori (i, 1, m) {
+ i32 subst_cost = str1[i - 1] == str2[j - 1] ? 0 : 1;
+
+ i32 a = d[(i - 1) * n + j] + 1;
+ i32 b = d[i * n + (j - 1)] + 1;
+ i32 c = d[(i - 1) * n + (j - 1)] + subst_cost;
+
+ d[i * n + j] = bh_min(bh_min(a, b), c);
+ }
+ }
+
+ return d[m * n - 1];
+}
+
+char *find_closest_symbol_in_scope(Scope *scope, char *sym, u32 *out_distance) {
+ *out_distance = 0x7fffffff;
+
+ if (scope == NULL) return NULL;
+
+ char* closest = NULL;
+ fori (i, 0, shlen(scope->symbols)) {
+ if (scope->symbols[i].value->flags & Ast_Flag_Symbol_Invisible) continue;
+
+ char *key = scope->symbols[i].key;
+ u32 d = levenshtein_distance(key, sym);
+ if (d < *out_distance) {
+ *out_distance = d;
+ closest = (char *) key;
+ }
+ }
+
+ return closest;
+}
+
+char *find_closest_symbol_in_scope_and_parents(Scope *scope, char *sym) {
+ u32 min_dist = 0x7fffffff;
+ u32 tmp_dist;
+
+ char *closest = NULL;
+ while (scope != NULL) {
+ char *tmp_closest = find_closest_symbol_in_scope(scope, sym, &tmp_dist);
+ if (tmp_dist < min_dist) {
+ min_dist = tmp_dist;
+ closest = tmp_closest;
+ }
+
+ scope = scope->parent;
+ }
+
+ return closest;
+}
+
+char *find_closest_symbol_in_node(AstNode* node, char *sym) {
+ Scope *scope = get_scope_from_node(node);
+ if (!scope) {
+ if (node->kind == Ast_Kind_Poly_Call_Type) {
+ AstPolyCallType* pcall = (AstPolyCallType *) node;
+ return find_closest_symbol_in_node((AstNode *) pcall->callee, sym);
+ }
+
+ return NULL;
+ }
+
+ u32 dist;
+ return find_closest_symbol_in_scope(scope, sym, &dist);
+}
+
--- /dev/null
+//
+// There are several things I'm seeing in this file that I want to clean up.
+// They are:
+// [x] remove the need to know if the stack is needed before generating the function.
+// Just leave 5 nops at the beginning because they will be automatically removed
+// by the WASM outputter.
+// [x] remove the need to have "allocate_exprs" on blocks and in functions. This will
+// be easy once the above is done.
+// [x] there should be a better way to emit pending deferred statements because there
+// is some code duplication between emit_return and emit_structured_jump.
+// [ ] Change the calling convention so it is easier to use from both JS and in the compiler.
+
+
+
+
+#define BH_DEBUG
+#include "wasm_emit.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) {
+ if (type_linear_member_count(type) == 1) {
+ return onyx_type_to_wasm_type(type->Struct.linear_members[0].type);
+ }
+
+ return WASM_TYPE_VOID;
+ }
+
+ if (type->kind == Type_Kind_Slice) {
+ return WASM_TYPE_VOID;
+ }
+
+ if (type->kind == Type_Kind_Compound) {
+ return WASM_TYPE_VOID;
+ }
+
+ if (type->kind == Type_Kind_Enum) {
+ return onyx_type_to_wasm_type(type->Enum.backing);
+ }
+
+ if (type->kind == Type_Kind_Distinct) {
+ return onyx_type_to_wasm_type(type->Distinct.base_type);
+ }
+
+ 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_Distinct && local->type->Distinct.base_type->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;
+}
+
+
+static inline b32 should_emit_function(AstFunction* fd) {
+ // NOTE: Don't output intrinsic functions
+ if (fd->is_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->is_exported || bh_arr_length(fd->tags) > 0) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+//
+// Debug Info Generation
+//
+
+#ifdef ENABLE_DEBUG_INFO
+
+static u32 debug_introduce_symbol(OnyxWasmModule *mod, OnyxToken *token, DebugSymLoc loc, u64 num, Type* type) {
+
+ u32 id = mod->debug_context->next_sym_id++;
+
+ DebugSymInfo sym_info;
+ sym_info.sym_id = id;
+ sym_info.location_type = loc;
+ sym_info.location_num = num;
+ sym_info.type = type->id;
+
+ if (token) {
+ token_toggle_end(token);
+ sym_info.name = bh_strdup(context.ast_alloc, token->text);
+ token_toggle_end(token);
+ } else {
+ sym_info.name = NULL;
+ }
+
+ bh_arr_push(mod->debug_context->sym_info, sym_info);
+
+ if (loc == DSL_REGISTER) {
+ assert(mod->local_alloc);
+ DebugSymPatch patch;
+ patch.func_idx = mod->current_func_idx;
+ patch.sym_id = id;
+ patch.local_idx = num;
+ bh_arr_push(mod->debug_context->sym_patches, patch);
+ }
+
+ bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_SYM);
+ u32 leb_len=0;
+ u8 *bytes = uint_to_uleb128(id, &leb_len);
+ bh_buffer_append(&mod->debug_context->op_buffer, bytes, leb_len);
+
+ mod->debug_context->last_op_was_rep = 0;
+ return id;
+}
+
+static u32 debug_get_file_id(OnyxWasmModule *mod, const char *name) {
+ assert(mod && mod->debug_context);
+
+ i32 index = shgeti(mod->debug_context->file_info, name);
+ if (index == -1) {
+ u32 id = mod->debug_context->next_file_id++;
+ DebugFileInfo file_info;
+ file_info.file_id = id;
+
+ bh_arr_each(bh_file_contents, fc, context.loaded_files) {
+ if (!strcmp(fc->filename, name)) {
+ file_info.line_count = fc->line_count;
+ }
+ }
+ shput(mod->debug_context->file_info, name, file_info);
+
+ return id;
+ }
+
+ return mod->debug_context->file_info[index].value.file_id;
+}
+
+static void debug_set_position(OnyxWasmModule *mod, OnyxToken *token) {
+ i32 file_id = debug_get_file_id(mod, token->pos.filename);
+
+ bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_SET);
+ mod->debug_context->last_op_was_rep = 0;
+
+ u32 leb_len=0;
+ u8 *bytes = uint_to_uleb128(file_id, &leb_len);
+ bh_buffer_append(&mod->debug_context->op_buffer, bytes, leb_len);
+
+ bytes = uint_to_uleb128(token->pos.line, &leb_len);
+ bh_buffer_append(&mod->debug_context->op_buffer, bytes, leb_len);
+
+ mod->debug_context->last_token = token;
+}
+
+// Called for every instruction being emitted
+// This has to emit either:
+// - INC
+// - DEC
+// - REP
+// - SET, REP 0
+static void debug_emit_instruction(OnyxWasmModule *mod, OnyxToken *token) {
+ DebugContext *ctx = mod->debug_context;
+ assert(ctx && ctx->last_token);
+
+ // Sanity check
+ if (ctx->last_op_was_rep) {
+ assert((ctx->op_buffer.data[ctx->op_buffer.length - 1] & DOT_REP) == DOT_REP);
+ }
+ i32 file_id, old_file_id;
+
+ b32 repeat_previous = 0;
+ if (!token || !token->pos.filename) {
+ repeat_previous = 1;
+
+ } else {
+ file_id = debug_get_file_id(mod, token->pos.filename);
+ old_file_id = debug_get_file_id(mod, ctx->last_token->pos.filename);
+ if (old_file_id == file_id && token->pos.line == ctx->last_token->pos.line) {
+ repeat_previous = 1;
+ }
+ }
+
+ if (repeat_previous) {
+ // Output / increment REP instruction
+ if (ctx->last_op_was_rep) {
+ u8 rep_count = ctx->op_buffer.data[ctx->op_buffer.length - 1] & 0b00111111;
+ if (rep_count != 63) {
+ rep_count += 1;
+ ctx->op_buffer.data[ctx->op_buffer.length - 1] = DOT_REP | rep_count;
+ return;
+ }
+ }
+
+ bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_REP);
+ ctx->last_op_was_rep = 1;
+ return;
+ }
+
+ // At this point, token is definitely non-null.
+ ctx->last_op_was_rep = 0;
+
+ if (old_file_id == file_id) {
+ // We see if we can INC/DEC to get to the line number
+ if (ctx->last_token->pos.line < token->pos.line) {
+ u32 diff = token->pos.line - ctx->last_token->pos.line;
+ if (diff <= 64) {
+ bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_INC | (diff - 1));
+ goto done;
+ }
+ }
+
+ if (ctx->last_token->pos.line > token->pos.line) {
+ u32 diff = ctx->last_token->pos.line - token->pos.line;
+ if (diff <= 64) {
+ bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_DEC | (diff - 1));
+ goto done;
+ }
+ }
+ }
+
+ // Otherwise, we need to output a SET, followed by a REP 0,
+ // which is what set_position does.
+ debug_set_position(mod, token);
+ bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_REP);
+ ctx->last_op_was_rep = 1;
+
+ done:
+ ctx->last_token = token;
+}
+
+static void debug_begin_function(OnyxWasmModule *mod, u32 func_idx, OnyxToken *token, char *name) {
+ u32 file_id = debug_get_file_id(mod, token->pos.filename);
+ u32 line = token->pos.line;
+
+ assert(mod->debug_context);
+
+ DebugFuncContext func;
+ func.func_index = func_idx;
+ func.file_id = file_id;
+ func.line = line;
+ func.op_offset = mod->debug_context->op_buffer.length;
+ func.stack_ptr_idx = 0;
+ func.name_length = strlen(name);
+ func.name = name;
+ bh_arr_push(mod->debug_context->funcs, func);
+
+ bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_PUSHF);
+ debug_set_position(mod, token);
+}
+
+static void debug_end_function(OnyxWasmModule *mod) {
+ bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_POPF);
+ bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_END);
+ mod->debug_context->last_op_was_rep = 0;
+ mod->debug_context->last_token = NULL;
+}
+
+static void debug_enter_symbol_frame(OnyxWasmModule *mod) {
+ bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_PUSHF);
+ mod->debug_context->last_op_was_rep = 0;
+}
+
+static void debug_leave_symbol_frame(OnyxWasmModule *mod) {
+ bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_POPF);
+ mod->debug_context->last_op_was_rep = 0;
+}
+
+#else
+
+#define debug_introduce_symbol(mod, name, loc, num, type) (void)0
+#define debug_get_file_id(mod, name) (void)0
+#define debug_set_position(mod, token) (void)0
+#define debug_emit_instruction(mod, token) (void)0
+#define debug_begin_function(mod, idx, token, name) (void)0
+#define debug_end_function(mod) (void)0
+#define debug_enter_symbol_frame(mod) (void)0
+#define debug_leave_symbol_frame(mod) (void)0
+
+#endif
+
+
+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,
+};
+
+#ifdef ENABLE_DEBUG_INFO
+ #define WI(token, instr) (debug_emit_instruction(mod, token), bh_arr_push(code, ((WasmInstruction){ instr, 0x00 })))
+ #define WID(token, instr, data) (debug_emit_instruction(mod, token), bh_arr_push(code, ((WasmInstruction){ instr, data })))
+ #define WIL(token, instr, data) (debug_emit_instruction(mod, token), bh_arr_push(code, ((WasmInstruction){ instr, { .l = data } })))
+ #define WIP(token, instr, data) (debug_emit_instruction(mod, token), bh_arr_push(code, ((WasmInstruction){ instr, { .p = data } })))
+ #define WIR(token, full_instr) (debug_emit_instruction(mod, token), bh_arr_push(code, full_instr))
+#else
+ #define WI(token, instr) (bh_arr_push(code, ((WasmInstruction){ instr, 0x00 })))
+ #define WID(token, instr, data) (bh_arr_push(code, ((WasmInstruction){ instr, data })))
+ #define WIL(token, instr, data) (bh_arr_push(code, ((WasmInstruction){ instr, { .l = data } })))
+ #define WIP(token, instr, data) (bh_arr_push(code, ((WasmInstruction){ instr, { .p = data } })))
+ #define WIR(token, full_instr) (bh_arr_push(code, full_instr))
+#endif
+
+#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(token, type1, type2) { \
+ u64 t0 = local_raw_allocate(mod->local_alloc, type1); \
+ u64 t1 = local_raw_allocate(mod->local_alloc, type2); \
+ \
+ WIL(token, WI_LOCAL_SET, t0); \
+ WIL(token, WI_LOCAL_SET, t1); \
+ WIL(token, WI_LOCAL_GET, t0); \
+ WIL(token, 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 }))
+#define NEXT_DATA_ID(mod) ((u32) bh_arr_length((mod)->data) + 1)
+
+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(data_relocation, u32 data_id);
+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(remove_directive, AstDirectiveRemove* remove);
+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, OnyxToken* block_token);
+EMIT_FUNC_NO_ARGS(leave_structured_block);
+
+static u32 emit_data_entry(OnyxWasmModule *mod, WasmDatum *datum);
+
+static void emit_constexpr(ConstExprContext *ctx, AstTyped *node, u32 offset);
+static b32 emit_constexpr_(ConstExprContext *ctx, AstTyped *node, u32 offset);
+
+#include "wasm_intrinsics.h"
+#include "wasm_type_table.h"
+
+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,
+ block->token);
+ debug_enter_symbol_frame(mod);
+ }
+
+ 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);
+ debug_leave_symbol_frame(mod);
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(enter_structured_block, StructuredBlockType sbt, OnyxToken* token) {
+ 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(token, 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(NULL, 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(jump->token, WI_JUMP, labelidx);
+ } else {
+ onyx_report_error(jump->token->pos, Error_Critical, "Invalid structured jump.");
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(statement, AstNode* stmt) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+#ifdef ENABLE_DEBUG_INFO
+ debug_set_position(mod, stmt->token);
+#endif
+
+ 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_Remove: emit_remove_directive(mod, &code, (AstDirectiveRemove *) stmt); break;
+ case Ast_Kind_Directive_Insert: break;
+
+ default: emit_expression(mod, &code, (AstTyped *) stmt); break;
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(local_allocation, AstTyped* stmt) {
+ u64 local_idx = local_allocate(mod->local_alloc, stmt);
+ bh_imap_put(&mod->local_map, (u64) stmt, local_idx);
+
+ if (local_is_wasm_local(stmt)) {
+ debug_introduce_symbol(mod, stmt->token, DSL_REGISTER, local_idx, stmt->type);
+ } else {
+ debug_introduce_symbol(mod, stmt->token, DSL_STACK, local_idx, stmt->type);
+ }
+
+ if (stmt->kind == Ast_Kind_Local && !(stmt->flags & Ast_Flag_Decl_Followed_By_Init)) {
+ bh_arr(WasmInstruction) code = *pcode;
+ if (local_is_wasm_local(stmt)) {
+ emit_zero_value(mod, &code, onyx_type_to_wasm_type(stmt->type));
+ WIL(stmt->token, WI_LOCAL_SET, local_idx);
+
+ } else {
+ emit_location(mod, &code, stmt);
+ WID(stmt->token, WI_I32_CONST, 0);
+ WID(stmt->token, WI_I32_CONST, type_size_of(stmt->type));
+ if (context.options->use_post_mvp_features) {
+ WID(stmt->token, WI_MEMORY_FILL, 0x00);
+ } else {
+ emit_intrinsic_memory_fill(mod, &code);
+ }
+ }
+
+ *pcode = code;
+ }
+
+ 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(data_relocation, u32 data_id) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ u32 instr_idx = bh_arr_length(code);
+ WID(NULL, WI_PTR_CONST, 0);
+ assert(mod->current_func_idx >= 0);
+
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Instruction;
+ patch.index = mod->current_func_idx;
+ patch.location = instr_idx;
+ patch.data_id = data_id;
+ patch.offset = 0;
+ bh_arr_push(mod->data_patches, patch);
+
+ *pcode = code;
+}
+
+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(assign->token, WI_LOCAL_SET, localidx + i);
+
+ } else {
+ WIL(assign->token, WI_LOCAL_SET, localidx);
+ }
+
+ *pcode = code;
+ return;
+ }
+ }
+
+ if (lval->kind == Ast_Kind_Field_Access) {
+ AstFieldAccess* fa = (AstFieldAccess *) lval;
+ if (fa->expr->kind == Ast_Kind_Param && type_is_structlike_strict(fa->expr->type)) {
+ emit_expression(mod, &code, assign->right);
+
+ u64 localidx = bh_imap_get(&mod->local_map, (u64) fa->expr);
+ WIL(assign->token, WI_LOCAL_SET, localidx + fa->idx);
+
+ *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(assign->token, 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(left->token, WI_LOCAL_SET, lptr_local);
+
+ AstArrayLiteral* al = (AstArrayLiteral *) right;
+ fori (i, 0, elem_count) {
+ WIL(left->token, 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(assign->token, 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(assign->token, WI_LOCAL_SET, expr_tmp);
+ u64 offset = 0;
+ emit_location_return_offset(mod, &code, lval, &offset);
+ WIL(assign->token, 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_Struct) {
+ assert(bh_arr_length(type->Struct.linear_members) == 1);
+ type = type->Struct.linear_members[0].type;
+ }
+
+ 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_Distinct) type = type->Distinct.base_type;
+ 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(NULL, 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(NULL, WI_I32_STORE_8, ((WasmInstructionData) { alignment, offset }));
+ else if (store_size == 2) WID(NULL, WI_I32_STORE_16, ((WasmInstructionData) { alignment, offset }));
+ else if (store_size == 4) WID(NULL, WI_I32_STORE, ((WasmInstructionData) { alignment, offset }));
+ else if (store_size == 8) WID(NULL, WI_I64_STORE, ((WasmInstructionData) { alignment, offset }));
+ } else if (is_basic && (type->Basic.flags & Basic_Flag_Float)) {
+ if (store_size == 4) WID(NULL, WI_F32_STORE, ((WasmInstructionData) { alignment, offset }));
+ else if (store_size == 8) WID(NULL, WI_F64_STORE, ((WasmInstructionData) { alignment, offset }));
+ } else if (is_basic && (type->Basic.flags & Basic_Flag_SIMD)) {
+ WID(NULL, WI_V128_STORE, ((WasmInstructionData) { alignment, offset }));
+ } else {
+ onyx_report_error((OnyxFilePos) { 0 }, Error_Critical,
+ "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_Struct) {
+ assert(bh_arr_length(type->Struct.linear_members) == 1);
+ type = type->Struct.linear_members[0].type;
+ }
+
+ if (type->kind == Type_Kind_Array) {
+ if (offset != 0) {
+ WID(NULL, WI_PTR_CONST, offset);
+ WI(NULL, WI_PTR_ADD);
+ }
+
+ *pcode = code;
+ return;
+ }
+
+ if (type->kind == Type_Kind_Enum) type = type->Enum.backing;
+ if (type->kind == Type_Kind_Distinct) type = type->Distinct.base_type;
+ 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(NULL, instr, ((WasmInstructionData) { alignment, offset }));
+
+ if (instr == WI_NOP) {
+ onyx_report_error((OnyxFilePos) { 0 }, Error_Critical,
+ "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_node->token);
+ if (if_node->true_stmt) emit_block(mod, &code, if_node->true_stmt, 0);
+
+ if (if_node->false_stmt) {
+ WI(if_node->false_stmt->token, 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, while_node->token);
+ emit_enter_structured_block(mod, &code, SBT_Continue_Loop, while_node->token);
+
+ if (!while_node->bottom_test) {
+ emit_expression(mod, &code, while_node->cond);
+ WI(NULL, WI_I32_EQZ);
+ WID(NULL, WI_COND_JUMP, 0x01);
+ }
+
+ emit_block(mod, &code, while_node->true_stmt, 0);
+
+ if (while_node->bottom_test) {
+ emit_expression(mod, &code, while_node->cond);
+ WID(while_node->cond->token, WI_COND_JUMP, 0x00);
+
+ } else {
+ if (bh_arr_last(code).type != WI_JUMP)
+ WID(while_node->cond->token, 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, while_node->token);
+ emit_enter_structured_block(mod, &code, SBT_Continue_Loop, while_node->token);
+
+ emit_block(mod, &code, while_node->true_stmt, 0);
+
+ emit_expression(mod, &code, while_node->cond);
+ WID(while_node->cond->token, WI_COND_JUMP, 0x00);
+
+ emit_leave_structured_block(mod, &code);
+ WI(while_node->false_stmt->token, 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
+
+ // NOTE: This might not be a range literal
+ AstStructLiteral *range = (AstStructLiteral *) for_node->iter;
+ 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(for_node->token, WI_LOCAL_SET, step_local);
+ WIL(for_node->token, WI_LOCAL_SET, high_local);
+ WIL(for_node->token, WI_LOCAL_TEE, low_local);
+ WIL(for_node->token, WI_LOCAL_SET, iter_local);
+
+ emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
+ emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token);
+ emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token);
+
+ if (range->kind == Ast_Kind_Struct_Literal && (range->args.values[2]->flags & Ast_Flag_Comptime) != 0) {
+ AstNumLit *step_value = (AstNumLit *) range->args.values[2];
+ assert(step_value->kind == Ast_Kind_NumLit);
+
+ if (step_value->value.l >= 0) {
+ WIL(for_node->token, WI_LOCAL_GET, iter_local);
+ WIL(for_node->token, WI_LOCAL_GET, high_local);
+ WI(for_node->token, WI_I32_GE_S);
+ WID(for_node->token, WI_COND_JUMP, 0x02);
+ } else {
+ WIL(for_node->token, WI_LOCAL_GET, iter_local);
+ WIL(for_node->token, WI_LOCAL_GET, high_local);
+ WI(for_node->token, WI_I32_LT_S);
+ WID(for_node->token, WI_COND_JUMP, 0x02);
+ }
+
+ } else {
+ WIL(for_node->token, WI_LOCAL_GET, step_local);
+ WID(for_node->token, WI_I32_CONST, 0);
+ WI(for_node->token, WI_I32_GE_S);
+ WID(for_node->token, WI_IF_START, 0x40);
+ WIL(for_node->token, WI_LOCAL_GET, iter_local);
+ WIL(for_node->token, WI_LOCAL_GET, high_local);
+ WI(for_node->token, WI_I32_GE_S);
+ WID(for_node->token, WI_COND_JUMP, 0x03);
+ WI(for_node->token, WI_ELSE);
+ WIL(for_node->token, WI_LOCAL_GET, iter_local);
+ WIL(for_node->token, WI_LOCAL_GET, high_local);
+ WI(for_node->token, WI_I32_LT_S);
+ WID(for_node->token, WI_COND_JUMP, 0x03);
+ WI(for_node->token, WI_IF_END);
+ }
+
+
+ emit_block(mod, &code, for_node->stmt, 0);
+
+ emit_leave_structured_block(mod, &code);
+
+ WIL(for_node->token, WI_LOCAL_GET, iter_local);
+ WIL(for_node->token, WI_LOCAL_GET, step_local);
+ WI(for_node->token, WI_I32_ADD);
+ WIL(for_node->token, WI_LOCAL_SET, iter_local);
+
+ if (bh_arr_last(code).type != WI_JUMP)
+ WID(for_node->token, 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(for_node->token, WI_LOCAL_TEE, ptr_local);
+ WIL(for_node->token, WI_PTR_CONST, for_node->iter->type->Array.count * elem_size);
+ WI(for_node->token, WI_PTR_ADD);
+ WIL(for_node->token, WI_LOCAL_SET, end_ptr_local);
+
+ emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
+ emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token);
+ emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token);
+
+ WIL(for_node->token, WI_LOCAL_GET, ptr_local);
+ WIL(for_node->token, WI_LOCAL_GET, end_ptr_local);
+ WI(for_node->token, WI_PTR_GE);
+ WID(for_node->token, WI_COND_JUMP, 0x02);
+
+ if (!for_node->by_pointer) {
+ if (!it_is_local) emit_local_location(mod, &code, var, &offset);
+
+ WIL(for_node->token, 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(for_node->token, WI_LOCAL_SET, iter_local);
+ }
+
+ emit_block(mod, &code, for_node->stmt, 0);
+
+ emit_leave_structured_block(mod, &code);
+
+ WIL(for_node->token, WI_LOCAL_GET, ptr_local);
+ WIL(for_node->token, WI_PTR_CONST, elem_size);
+ WI(for_node->token, WI_PTR_ADD);
+ WIL(for_node->token, WI_LOCAL_SET, ptr_local);
+
+ if (bh_arr_last(code).type != WI_JUMP)
+ WID(for_node->token, 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(for_node->token, WI_LOCAL_SET, end_ptr_local);
+ WIL(for_node->token, WI_LOCAL_TEE, ptr_local);
+ WIL(for_node->token, WI_LOCAL_GET, end_ptr_local);
+ if (elem_size != 1) {
+ WID(for_node->token, WI_PTR_CONST, elem_size);
+ WI(for_node->token, WI_PTR_MUL);
+ }
+ WI(for_node->token, WI_PTR_ADD);
+ WIL(for_node->token, WI_LOCAL_SET, end_ptr_local);
+
+ emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
+ emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token);
+ emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token);
+
+ WIL(for_node->token, WI_LOCAL_GET, ptr_local);
+ WIL(for_node->token, WI_LOCAL_GET, end_ptr_local);
+ WI(for_node->token, WI_PTR_GE);
+ WID(for_node->token, WI_COND_JUMP, 0x02);
+
+ if (!for_node->by_pointer) {
+ if (!it_is_local) emit_local_location(mod, &code, var, &offset);
+
+ WIL(for_node->token, 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(for_node->token, WI_LOCAL_SET, iter_local);
+ }
+
+ emit_block(mod, &code, for_node->stmt, 0);
+
+ emit_leave_structured_block(mod, &code);
+
+ WIL(for_node->token, WI_LOCAL_GET, ptr_local);
+ WIL(for_node->token, WI_PTR_CONST, elem_size);
+ WI(for_node->token, WI_PTR_ADD);
+ WIL(for_node->token, WI_LOCAL_SET, ptr_local);
+
+ if (bh_arr_last(code).type != WI_JUMP)
+ WID(for_node->token, 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_remove_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
+ u64 iterator_done_bool = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+ WIL(for_node->token, WI_LOCAL_SET, iterator_remove_func);
+ WIL(for_node->token, WI_LOCAL_SET, iterator_close_func);
+ WIL(for_node->token, WI_LOCAL_SET, iterator_next_func);
+ WIL(for_node->token, WI_LOCAL_SET, iterator_data_ptr);
+
+
+ {
+ //
+ // This pushes an entry onto the stack of for loops that have
+ // are iterator that can have a '#remove' directive in them.
+ ForRemoveInfo remove_info;
+ remove_info.iterator_data_ptr = iterator_data_ptr;
+ remove_info.iterator_remove_func = iterator_remove_func;
+
+ TypeWithOffset remove_func_type;
+ type_linear_member_lookup(for_node->iter->type, 3, &remove_func_type);
+ remove_info.remove_func_type_idx = generate_type_idx(mod, remove_func_type.type);
+
+ bh_arr_push(mod->for_remove_info, remove_info);
+ }
+
+ 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, for_node->token);
+
+ if (!for_node->no_close) {
+ 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, for_node->token);
+ emit_enter_structured_block(mod, &code, SBT_Continue_Loop, for_node->token);
+
+ if (!it_is_local) emit_local_location(mod, &code, var, &offset);
+
+ {
+ WIL(for_node->token, WI_LOCAL_GET, iterator_data_ptr);
+ WIL(for_node->token, 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(for_node->token, WI_GLOBAL_GET, stack_top_idx);
+ WID(for_node->token, WI_PTR_CONST, reserve_size);
+ WI(for_node->token, WI_PTR_ADD);
+ WID(for_node->token, WI_GLOBAL_SET, stack_top_idx);
+
+ i32 type_idx = generate_type_idx(mod, next_func_type.type);
+ WID(for_node->token, WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
+
+ WID(for_node->token, WI_GLOBAL_GET, stack_top_idx);
+ WID(for_node->token, WI_PTR_CONST, reserve_size);
+ WI(for_node->token, WI_PTR_SUB);
+ WID(for_node->token, WI_GLOBAL_SET, stack_top_idx);
+
+ WID(for_node->token, WI_GLOBAL_GET, stack_top_idx);
+ emit_load_instruction(mod, &code, return_type, reserve_size - return_size);
+ }
+
+ WIL(for_node->token, WI_LOCAL_SET, iterator_done_bool);
+
+ if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
+ else WIL(for_node->token, WI_LOCAL_SET, iter_local);
+
+ WIL(for_node->token, WI_LOCAL_GET, iterator_done_bool);
+ WI(for_node->token, WI_I32_EQZ);
+ WID(for_node->token, WI_COND_JUMP, 0x01);
+
+ emit_block(mod, &code, for_node->stmt, 0);
+ WID(for_node->token, 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);
+
+ bh_arr_pop(mod->for_remove_info);
+
+ 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);
+
+ debug_enter_symbol_frame(mod);
+ debug_introduce_symbol(mod, var->token,
+ local_is_wasm_local((AstTyped *) var) ? DSL_REGISTER : DSL_STACK,
+ iter_local, var->type);
+
+ 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(for_node->token, WI_DROP); WI(for_node->token, WI_DROP); WI(for_node->token, 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, Error_Critical, "Invalid for loop type. You should probably not be seeing this...");
+ }
+
+ local_free(mod->local_alloc, (AstTyped *) var);
+ debug_leave_symbol_frame(mod);
+
+ *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, switch_node->token);
+
+ 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, (*sc)->block->token);
+
+ bh_imap_put(&block_map, (u64) (*sc)->block, block_num);
+ block_num++;
+ }
+
+ switch (switch_node->switch_kind) {
+ case Switch_Kind_Integer: {
+ 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);
+ }
+
+ // NOTE: We enter a new block here in order to setup the correct
+ // indicies for the jump targets in the branch table. For example,
+ //
+ // <expr>
+ // jump_table
+ // label0:
+ // ...
+ // label1:
+ // ...
+ //
+ // If we didn't enter a new block, then jumping to label 0, would jump
+ // to the second block, and so on.
+ WID(switch_node->expr->token, WI_BLOCK_START, 0x40);
+ emit_expression(mod, &code, switch_node->expr);
+ if (switch_node->min_case != 0) {
+ WID(switch_node->expr->token, WI_I32_CONST, switch_node->min_case);
+ WI(switch_node->expr->token, WI_I32_SUB);
+ }
+ WIP(switch_node->expr->token, WI_JUMP_TABLE, bt);
+ WI(switch_node->expr->token, WI_BLOCK_END);
+ break;
+ }
+
+ case Switch_Kind_Use_Equals: {
+ WID(switch_node->expr->token, WI_BLOCK_START, 0x40);
+
+ bh_arr_each(CaseToBlock, ctb, switch_node->case_exprs) {
+ emit_expression(mod, &code, (AstTyped *) ctb->comparison);
+
+ u64 bn = bh_imap_get(&block_map, (u64) ctb->block);
+ WID(switch_node->expr->token, WI_IF_START, 0x40);
+ WID(switch_node->expr->token, WI_JUMP, bn + 1);
+ WI(switch_node->expr->token, WI_IF_END);
+ }
+
+ WID(switch_node->expr->token, WI_JUMP, block_num);
+ WI(switch_node->expr->token, WI_BLOCK_END);
+ break;
+ }
+ }
+
+ bh_arr_each(AstSwitchCase *, psc, switch_node->cases) {
+ AstSwitchCase *sc = *psc;
+ if (bh_imap_get(&block_map, (u64) sc->block) == 0xdeadbeef) continue;
+
+ u64 bn = bh_imap_get(&block_map, (u64) sc->block);
+
+ // Maybe the Symbol Frame idea should be controlled as a block_flag?
+ debug_enter_symbol_frame(mod);
+ emit_block(mod, &code, sc->block, 0);
+ debug_leave_symbol_frame(mod);
+
+ if (bh_arr_last(code).type != WI_JUMP)
+ WID(NULL, 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),
+ .defer_node= defer,
+ .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),
+ .defer_node= NULL,
+ .instructions = deferred_code,
+ .instruction_count = code_count,
+ }));
+}
+
+EMIT_FUNC(deferred_stmt, DeferredStmt deferred_stmt) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ if (deferred_stmt.defer_node) {
+ WI(deferred_stmt.defer_node->token, WI_NOP);
+ }
+
+ 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) {
+ WIR(NULL, 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);
+ }
+}
+
+EMIT_FUNC(remove_directive, AstDirectiveRemove* remove) {
+ assert(bh_arr_length(mod->for_remove_info) > 0);
+
+ bh_arr(WasmInstruction) code = *pcode;
+
+ ForRemoveInfo remove_info = bh_arr_last(mod->for_remove_info);
+
+ WIL(remove->token, WI_LOCAL_GET, remove_info.iterator_remove_func);
+ WIL(remove->token, WI_I32_CONST, mod->null_proc_func_idx);
+ WI(remove->token, WI_I32_NE);
+ WID(remove->token, WI_IF_START, 0x40);
+ WIL(remove->token, WI_LOCAL_GET, remove_info.iterator_data_ptr);
+ WIL(remove->token, WI_LOCAL_GET, remove_info.iterator_remove_func);
+ WIL(remove->token, WI_CALL_INDIRECT, remove_info.remove_func_type_idx);
+ WI(remove->token, WI_IF_END);
+
+ *pcode = code;
+}
+
+// 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->token, 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(unop->token, WI_I32_CONST, 0x00);
+ emit_expression(mod, &code, unop->expr);
+ WI(unop->token, WI_I32_SUB);
+
+ }
+ else if (type->kind == Basic_Kind_I64) {
+ WID(unop->token, WI_I64_CONST, 0x00);
+ emit_expression(mod, &code, unop->expr);
+ WI(unop->token, WI_I64_SUB);
+
+ }
+ else {
+ emit_expression(mod, &code, unop->expr);
+
+ if (type->kind == Basic_Kind_F32) WI(unop->token, WI_F32_NEG);
+ if (type->kind == Basic_Kind_F64) WI(unop->token, WI_F64_NEG);
+ }
+
+ break;
+ }
+
+ case Unary_Op_Not:
+ emit_expression(mod, &code, unop->expr);
+
+ WI(unop->token, 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(unop->token, WI_I32_CONST, 0xff);
+ WI(unop->token, WI_I32_XOR);
+ }
+ else if (type->kind == Basic_Kind_I16 || type->kind == Basic_Kind_U16) {
+ WID(unop->token, WI_I32_CONST, 0xffff);
+ WI(unop->token, WI_I32_XOR);
+ }
+ else if (type->kind == Basic_Kind_I32 || type->kind == Basic_Kind_U32) {
+ WID(unop->token, WI_I32_CONST, 0xffffffff);
+ WI(unop->token, WI_I32_XOR);
+ }
+ else if (type->kind == Basic_Kind_I64 || type->kind == Basic_Kind_U64) {
+ WIL(unop->token, WI_I64_CONST, 0xffffffffffffffff);
+ WI(unop->token, 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);
+
+ OnyxToken* call_token = call->token;
+
+ // 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(call_token, WI_GLOBAL_GET, stack_top_idx);
+ WIL(call_token, WI_LOCAL_TEE, stack_top_store_local);
+ WID(call_token, WI_PTR_CONST, 0); // This will be filled in later.
+ WI(call_token, WI_PTR_ADD);
+ WID(call_token, 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 (arg->pass_as_any) {
+ place_on_stack = 1;
+ }
+
+ if (place_on_stack) WIL(call_token, 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(call_token, WI_LOCAL_GET, stack_top_store_local);
+ WID(call_token, WI_PTR_CONST, reserve_size);
+ WI(call_token, 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;
+ }
+
+ if (arg->pass_as_any) {
+ WIL(call_token, WI_I32_CONST, 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(call_token, WI_LOCAL_GET, stack_top_store_local);
+ WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+ WID(call_token, WI_PTR_CONST, vararg_any_offsets[i]);
+ WI(call_token, WI_PTR_ADD);
+ emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], vararg_offset + i * any_size);
+
+ WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+ WID(call_token, WI_I32_CONST, vararg_any_types[i]);
+ emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Type_Index], vararg_offset + i * any_size + POINTER_SIZE);
+
+ reserve_size += any_size;
+ }
+
+ // fallthrough
+ }
+
+ case VA_Kind_Typed: {
+ WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+ if (vararg_offset > 0) {
+ WID(call_token, WI_PTR_CONST, vararg_offset);
+ WI(call_token, WI_PTR_ADD);
+ }
+ WID(call_token, WI_I32_CONST, vararg_count);
+ break;
+ }
+
+ case VA_Kind_Untyped: {
+ WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+ WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+ if (vararg_offset > 0) {
+ WID(call_token, WI_PTR_CONST, vararg_offset);
+ WI(call_token, WI_PTR_ADD);
+ }
+ emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], reserve_size);
+
+ // NOTE: There may be 4 uninitialized bytes here, because pointers are only 4 bytes in WASM.
+
+ WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+ WID(call_token, WI_I32_CONST, vararg_count);
+ emit_store_instruction(mod, &code, &basic_types[Basic_Kind_I32], reserve_size + POINTER_SIZE);
+
+ WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+ if (reserve_size > 0) {
+ WID(call_token, WI_PTR_CONST, reserve_size);
+ WI(call_token, WI_PTR_ADD);
+ }
+
+ reserve_size += 4 + POINTER_SIZE;
+ 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);
+ WIL(NULL, WI_CALL, func_idx);
+
+ } else {
+ emit_expression(mod, &code, call->callee);
+
+ i32 type_idx = generate_type_idx(mod, call->callee->type);
+ WID(NULL, WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
+ }
+
+ if (reserve_size > 0) {
+ WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
+ WID(call_token, 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(call_token, 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, Error_Critical, \
+ "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(call->token, 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, Error_Critical, "SIMD lane instructions expect a compile time lane number."); \
+ *pcode = code; \
+ return; \
+ } \
+ WID(call->token, 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, Error_Critical, "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(call->token, 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(call->token, WI_MEMORY_SIZE, 0x00); break;
+ case ONYX_INTRINSIC_MEMORY_GROW: WID(call->token, WI_MEMORY_GROW, 0x00); break;
+ case ONYX_INTRINSIC_MEMORY_COPY:
+ if (context.options->use_post_mvp_features) {
+ WIL(call->token, 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(call->token, 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_I32_CLZ: WI(call->token, WI_I32_CLZ); break;
+ case ONYX_INTRINSIC_I32_CTZ: WI(call->token, WI_I32_CTZ); break;
+ case ONYX_INTRINSIC_I32_POPCNT: WI(call->token, WI_I32_POPCNT); break;
+ case ONYX_INTRINSIC_I32_AND: WI(call->token, WI_I32_AND); break;
+ case ONYX_INTRINSIC_I32_OR: WI(call->token, WI_I32_OR); break;
+ case ONYX_INTRINSIC_I32_XOR: WI(call->token, WI_I32_XOR); break;
+ case ONYX_INTRINSIC_I32_SHL: WI(call->token, WI_I32_SHL); break;
+ case ONYX_INTRINSIC_I32_SLR: WI(call->token, WI_I32_SHR_U); break;
+ case ONYX_INTRINSIC_I32_SAR: WI(call->token, WI_I32_SHR_S); break;
+ case ONYX_INTRINSIC_I32_ROTL: WI(call->token, WI_I32_ROTL); break;
+ case ONYX_INTRINSIC_I32_ROTR: WI(call->token, WI_I32_ROTR); break;
+
+ case ONYX_INTRINSIC_I64_CLZ: WI(call->token, WI_I64_CLZ); break;
+ case ONYX_INTRINSIC_I64_CTZ: WI(call->token, WI_I64_CTZ); break;
+ case ONYX_INTRINSIC_I64_POPCNT: WI(call->token, WI_I64_POPCNT); break;
+ case ONYX_INTRINSIC_I64_AND: WI(call->token, WI_I64_AND); break;
+ case ONYX_INTRINSIC_I64_OR: WI(call->token, WI_I64_OR); break;
+ case ONYX_INTRINSIC_I64_XOR: WI(call->token, WI_I64_XOR); break;
+ case ONYX_INTRINSIC_I64_SHL: WI(call->token, WI_I64_SHL); break;
+ case ONYX_INTRINSIC_I64_SLR: WI(call->token, WI_I64_SHR_U); break;
+ case ONYX_INTRINSIC_I64_SAR: WI(call->token, WI_I64_SHR_S); break;
+ case ONYX_INTRINSIC_I64_ROTL: WI(call->token, WI_I64_ROTL); break;
+ case ONYX_INTRINSIC_I64_ROTR: WI(call->token, WI_I64_ROTR); break;
+
+ case ONYX_INTRINSIC_F32_ABS: WI(call->token, WI_F32_ABS); break;
+ case ONYX_INTRINSIC_F32_CEIL: WI(call->token, WI_F32_CEIL); break;
+ case ONYX_INTRINSIC_F32_FLOOR: WI(call->token, WI_F32_FLOOR); break;
+ case ONYX_INTRINSIC_F32_TRUNC: WI(call->token, WI_F32_TRUNC); break;
+ case ONYX_INTRINSIC_F32_NEAREST: WI(call->token, WI_F32_NEAREST); break;
+ case ONYX_INTRINSIC_F32_SQRT: WI(call->token, WI_F32_SQRT); break;
+ case ONYX_INTRINSIC_F32_MIN: WI(call->token, WI_F32_MIN); break;
+ case ONYX_INTRINSIC_F32_MAX: WI(call->token, WI_F32_MAX); break;
+ case ONYX_INTRINSIC_F32_COPYSIGN: WI(call->token, WI_F32_COPYSIGN); break;
+
+ case ONYX_INTRINSIC_F64_ABS: WI(call->token, WI_F64_ABS); break;
+ case ONYX_INTRINSIC_F64_CEIL: WI(call->token, WI_F64_CEIL); break;
+ case ONYX_INTRINSIC_F64_FLOOR: WI(call->token, WI_F64_FLOOR); break;
+ case ONYX_INTRINSIC_F64_TRUNC: WI(call->token, WI_F64_TRUNC); break;
+ case ONYX_INTRINSIC_F64_NEAREST: WI(call->token, WI_F64_NEAREST); break;
+ case ONYX_INTRINSIC_F64_SQRT: WI(call->token, WI_F64_SQRT); break;
+ case ONYX_INTRINSIC_F64_MIN: WI(call->token, WI_F64_MIN); break;
+ case ONYX_INTRINSIC_F64_MAX: WI(call->token, WI_F64_MAX); break;
+ case ONYX_INTRINSIC_F64_COPYSIGN: WI(call->token, 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, Error_Critical,
+ "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(call->token, 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, Error_Critical,
+ "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(call->token, 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, Error_Critical,
+ "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(call->token, 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(call->token, WI_I8X16_SWIZZLE); break;
+ case ONYX_INTRINSIC_I8X16_SPLAT: WI(call->token, WI_I8X16_SPLAT); break;
+ case ONYX_INTRINSIC_I16X8_SPLAT: WI(call->token, WI_I16X8_SPLAT); break;
+ case ONYX_INTRINSIC_I32X4_SPLAT: WI(call->token, WI_I32X4_SPLAT); break;
+ case ONYX_INTRINSIC_I64X2_SPLAT: WI(call->token, WI_I64X2_SPLAT); break;
+ case ONYX_INTRINSIC_F32X4_SPLAT: WI(call->token, WI_F32X4_SPLAT); break;
+ case ONYX_INTRINSIC_F64X2_SPLAT: WI(call->token, WI_F64X2_SPLAT); break;
+
+ case ONYX_INTRINSIC_I8X16_EQ: WI(call->token, WI_I8X16_EQ); break;
+ case ONYX_INTRINSIC_I8X16_NEQ: WI(call->token, WI_I8X16_NEQ); break;
+ case ONYX_INTRINSIC_I8X16_LT_S: WI(call->token, WI_I8X16_LT_S); break;
+ case ONYX_INTRINSIC_I8X16_LT_U: WI(call->token, WI_I8X16_LT_U); break;
+ case ONYX_INTRINSIC_I8X16_GT_S: WI(call->token, WI_I8X16_GT_S); break;
+ case ONYX_INTRINSIC_I8X16_GT_U: WI(call->token, WI_I8X16_GT_U); break;
+ case ONYX_INTRINSIC_I8X16_LE_S: WI(call->token, WI_I8X16_LE_S); break;
+ case ONYX_INTRINSIC_I8X16_LE_U: WI(call->token, WI_I8X16_LE_U); break;
+ case ONYX_INTRINSIC_I8X16_GE_S: WI(call->token, WI_I8X16_GE_S); break;
+ case ONYX_INTRINSIC_I8X16_GE_U: WI(call->token, WI_I8X16_GE_U); break;
+
+ case ONYX_INTRINSIC_I16X8_EQ: WI(call->token, WI_I16X8_EQ); break;
+ case ONYX_INTRINSIC_I16X8_NEQ: WI(call->token, WI_I16X8_NEQ); break;
+ case ONYX_INTRINSIC_I16X8_LT_S: WI(call->token, WI_I16X8_LT_S); break;
+ case ONYX_INTRINSIC_I16X8_LT_U: WI(call->token, WI_I16X8_LT_U); break;
+ case ONYX_INTRINSIC_I16X8_GT_S: WI(call->token, WI_I16X8_GT_S); break;
+ case ONYX_INTRINSIC_I16X8_GT_U: WI(call->token, WI_I16X8_GT_U); break;
+ case ONYX_INTRINSIC_I16X8_LE_S: WI(call->token, WI_I16X8_LE_S); break;
+ case ONYX_INTRINSIC_I16X8_LE_U: WI(call->token, WI_I16X8_LE_U); break;
+ case ONYX_INTRINSIC_I16X8_GE_S: WI(call->token, WI_I16X8_GE_S); break;
+ case ONYX_INTRINSIC_I16X8_GE_U: WI(call->token, WI_I16X8_GE_U); break;
+
+ case ONYX_INTRINSIC_I32X4_EQ: WI(call->token, WI_I32X4_EQ); break;
+ case ONYX_INTRINSIC_I32X4_NEQ: WI(call->token, WI_I32X4_NEQ); break;
+ case ONYX_INTRINSIC_I32X4_LT_S: WI(call->token, WI_I32X4_LT_S); break;
+ case ONYX_INTRINSIC_I32X4_LT_U: WI(call->token, WI_I32X4_LT_U); break;
+ case ONYX_INTRINSIC_I32X4_GT_S: WI(call->token, WI_I32X4_GT_S); break;
+ case ONYX_INTRINSIC_I32X4_GT_U: WI(call->token, WI_I32X4_GT_U); break;
+ case ONYX_INTRINSIC_I32X4_LE_S: WI(call->token, WI_I32X4_LE_S); break;
+ case ONYX_INTRINSIC_I32X4_LE_U: WI(call->token, WI_I32X4_LE_U); break;
+ case ONYX_INTRINSIC_I32X4_GE_S: WI(call->token, WI_I32X4_GE_S); break;
+ case ONYX_INTRINSIC_I32X4_GE_U: WI(call->token, WI_I32X4_GE_U); break;
+
+ case ONYX_INTRINSIC_F32X4_EQ: WI(call->token, WI_F32X4_EQ); break;
+ case ONYX_INTRINSIC_F32X4_NEQ: WI(call->token, WI_F32X4_NEQ); break;
+ case ONYX_INTRINSIC_F32X4_LT: WI(call->token, WI_F32X4_LT); break;
+ case ONYX_INTRINSIC_F32X4_GT: WI(call->token, WI_F32X4_GT); break;
+ case ONYX_INTRINSIC_F32X4_LE: WI(call->token, WI_F32X4_LE); break;
+ case ONYX_INTRINSIC_F32X4_GE: WI(call->token, WI_F32X4_GE); break;
+
+ case ONYX_INTRINSIC_F64X2_EQ: WI(call->token, WI_F64X2_EQ); break;
+ case ONYX_INTRINSIC_F64X2_NEQ: WI(call->token, WI_F64X2_NEQ); break;
+ case ONYX_INTRINSIC_F64X2_LT: WI(call->token, WI_F64X2_LT); break;
+ case ONYX_INTRINSIC_F64X2_GT: WI(call->token, WI_F64X2_GT); break;
+ case ONYX_INTRINSIC_F64X2_LE: WI(call->token, WI_F64X2_LE); break;
+ case ONYX_INTRINSIC_F64X2_GE: WI(call->token, WI_F64X2_GE); break;
+
+ case ONYX_INTRINSIC_V128_NOT: WI(call->token, WI_V128_NOT); break;
+ case ONYX_INTRINSIC_V128_AND: WI(call->token, WI_V128_AND); break;
+ case ONYX_INTRINSIC_V128_ANDNOT: WI(call->token, WI_V128_ANDNOT); break;
+ case ONYX_INTRINSIC_V128_OR: WI(call->token, WI_V128_OR); break;
+ case ONYX_INTRINSIC_V128_XOR: WI(call->token, WI_V128_XOR); break;
+ case ONYX_INTRINSIC_V128_BITSELECT: WI(call->token, WI_V128_BITSELECT); break;
+
+ case ONYX_INTRINSIC_I8X16_ABS: WI(call->token, WI_I8X16_ABS); break;
+ case ONYX_INTRINSIC_I8X16_NEG: WI(call->token, WI_I8X16_NEG); break;
+ case ONYX_INTRINSIC_I8X16_ANY_TRUE: WI(call->token, WI_I8X16_ANY_TRUE); break;
+ case ONYX_INTRINSIC_I8X16_ALL_TRUE: WI(call->token, WI_I8X16_ALL_TRUE); break;
+ case ONYX_INTRINSIC_I8X16_BITMASK: WI(call->token, WI_I8X16_BITMASK); break;
+ case ONYX_INTRINSIC_I8X16_NARROW_I16X8_S: WI(call->token, WI_I8X16_NARROW_I16X8_S); break;
+ case ONYX_INTRINSIC_I8X16_NARROW_I16X8_U: WI(call->token, WI_I8X16_NARROW_I16X8_U); break;
+ case ONYX_INTRINSIC_I8X16_SHL: WI(call->token, WI_I8X16_SHL); break;
+ case ONYX_INTRINSIC_I8X16_SHR_S: WI(call->token, WI_I8X16_SHR_S); break;
+ case ONYX_INTRINSIC_I8X16_SHR_U: WI(call->token, WI_I8X16_SHR_U); break;
+ case ONYX_INTRINSIC_I8X16_ADD: WI(call->token, WI_I8X16_ADD); break;
+ case ONYX_INTRINSIC_I8X16_ADD_SAT_S: WI(call->token, WI_I8X16_ADD_SAT_S); break;
+ case ONYX_INTRINSIC_I8X16_ADD_SAT_U: WI(call->token, WI_I8X16_ADD_SAT_U); break;
+ case ONYX_INTRINSIC_I8X16_SUB: WI(call->token, WI_I8X16_SUB); break;
+ case ONYX_INTRINSIC_I8X16_SUB_SAT_S: WI(call->token, WI_I8X16_SUB_SAT_S); break;
+ case ONYX_INTRINSIC_I8X16_SUB_SAT_U: WI(call->token, WI_I8X16_SUB_SAT_U); break;
+ case ONYX_INTRINSIC_I8X16_MIN_S: WI(call->token, WI_I8X16_MIN_S); break;
+ case ONYX_INTRINSIC_I8X16_MIN_U: WI(call->token, WI_I8X16_MIN_U); break;
+ case ONYX_INTRINSIC_I8X16_MAX_S: WI(call->token, WI_I8X16_MAX_S); break;
+ case ONYX_INTRINSIC_I8X16_MAX_U: WI(call->token, WI_I8X16_MAX_U); break;
+ case ONYX_INTRINSIC_I8X16_AVGR_U: WI(call->token, WI_I8X16_AVGR_U); break;
+
+ case ONYX_INTRINSIC_I16X8_ABS: WI(call->token, WI_I16X8_ABS); break;
+ case ONYX_INTRINSIC_I16X8_NEG: WI(call->token, WI_I16X8_NEG); break;
+ case ONYX_INTRINSIC_I16X8_ANY_TRUE: WI(call->token, WI_I16X8_ANY_TRUE); break;
+ case ONYX_INTRINSIC_I16X8_ALL_TRUE: WI(call->token, WI_I16X8_ALL_TRUE); break;
+ case ONYX_INTRINSIC_I16X8_BITMASK: WI(call->token, WI_I16X8_BITMASK); break;
+ case ONYX_INTRINSIC_I16X8_NARROW_I32X4_S: WI(call->token, WI_I16X8_NARROW_I32X4_S); break;
+ case ONYX_INTRINSIC_I16X8_NARROW_I32X4_U: WI(call->token, WI_I16X8_NARROW_I32X4_U); break;
+ case ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_S: WI(call->token, WI_I16X8_WIDEN_LOW_I8X16_S); break;
+ case ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_S: WI(call->token, WI_I16X8_WIDEN_HIGH_I8X16_S); break;
+ case ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_U: WI(call->token, WI_I16X8_WIDEN_LOW_I8X16_U); break;
+ case ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_U: WI(call->token, WI_I16X8_WIDEN_HIGH_I8X16_U); break;
+ case ONYX_INTRINSIC_I16X8_SHL: WI(call->token, WI_I16X8_SHL); break;
+ case ONYX_INTRINSIC_I16X8_SHR_S: WI(call->token, WI_I16X8_SHR_S); break;
+ case ONYX_INTRINSIC_I16X8_SHR_U: WI(call->token, WI_I16X8_SHR_U); break;
+ case ONYX_INTRINSIC_I16X8_ADD: WI(call->token, WI_I16X8_ADD); break;
+ case ONYX_INTRINSIC_I16X8_ADD_SAT_S: WI(call->token, WI_I16X8_ADD_SAT_S); break;
+ case ONYX_INTRINSIC_I16X8_ADD_SAT_U: WI(call->token, WI_I16X8_ADD_SAT_U); break;
+ case ONYX_INTRINSIC_I16X8_SUB: WI(call->token, WI_I16X8_SUB); break;
+ case ONYX_INTRINSIC_I16X8_SUB_SAT_S: WI(call->token, WI_I16X8_SUB_SAT_S); break;
+ case ONYX_INTRINSIC_I16X8_SUB_SAT_U: WI(call->token, WI_I16X8_SUB_SAT_U); break;
+ case ONYX_INTRINSIC_I16X8_MUL: WI(call->token, WI_I16X8_MUL); break;
+ case ONYX_INTRINSIC_I16X8_MIN_S: WI(call->token, WI_I16X8_MIN_S); break;
+ case ONYX_INTRINSIC_I16X8_MIN_U: WI(call->token, WI_I16X8_MIN_U); break;
+ case ONYX_INTRINSIC_I16X8_MAX_S: WI(call->token, WI_I16X8_MAX_S); break;
+ case ONYX_INTRINSIC_I16X8_MAX_U: WI(call->token, WI_I16X8_MAX_U); break;
+ case ONYX_INTRINSIC_I16X8_AVGR_U: WI(call->token, WI_I16X8_AVGR_U); break;
+
+ case ONYX_INTRINSIC_I32X4_ABS: WI(call->token, WI_I32X4_ABS); break;
+ case ONYX_INTRINSIC_I32X4_NEG: WI(call->token, WI_I32X4_NEG); break;
+ case ONYX_INTRINSIC_I32X4_ANY_TRUE: WI(call->token, WI_I32X4_ANY_TRUE); break;
+ case ONYX_INTRINSIC_I32X4_ALL_TRUE: WI(call->token, WI_I32X4_ALL_TRUE); break;
+ case ONYX_INTRINSIC_I32X4_BITMASK: WI(call->token, WI_I32X4_BITMASK); break;
+ case ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_S: WI(call->token, WI_I32X4_WIDEN_LOW_I16X8_S); break;
+ case ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_S: WI(call->token, WI_I32X4_WIDEN_HIGH_I16X8_S); break;
+ case ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_U: WI(call->token, WI_I32X4_WIDEN_LOW_I16X8_U); break;
+ case ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_U: WI(call->token, WI_I32X4_WIDEN_HIGH_I16X8_U); break;
+ case ONYX_INTRINSIC_I32X4_SHL: WI(call->token, WI_I32X4_SHL); break;
+ case ONYX_INTRINSIC_I32X4_SHR_S: WI(call->token, WI_I32X4_SHR_S); break;
+ case ONYX_INTRINSIC_I32X4_SHR_U: WI(call->token, WI_I32X4_SHR_U); break;
+ case ONYX_INTRINSIC_I32X4_ADD: WI(call->token, WI_I32X4_ADD); break;
+ case ONYX_INTRINSIC_I32X4_SUB: WI(call->token, WI_I32X4_SUB); break;
+ case ONYX_INTRINSIC_I32X4_MUL: WI(call->token, WI_I32X4_MUL); break;
+ case ONYX_INTRINSIC_I32X4_MIN_S: WI(call->token, WI_I32X4_MIN_S); break;
+ case ONYX_INTRINSIC_I32X4_MIN_U: WI(call->token, WI_I32X4_MIN_U); break;
+ case ONYX_INTRINSIC_I32X4_MAX_S: WI(call->token, WI_I32X4_MAX_S); break;
+ case ONYX_INTRINSIC_I32X4_MAX_U: WI(call->token, WI_I32X4_MAX_U); break;
+
+ case ONYX_INTRINSIC_I64X2_NEG: WI(call->token, WI_I64X2_NEG); break;
+ case ONYX_INTRINSIC_I64X2_SHL: WI(call->token, WI_I64X2_SHL); break;
+ case ONYX_INTRINSIC_I64X2_SHR_S: WI(call->token, WI_I64X2_SHR_S); break;
+ case ONYX_INTRINSIC_I64X2_SHR_U: WI(call->token, WI_I64X2_SHR_U); break;
+ case ONYX_INTRINSIC_I64X2_ADD: WI(call->token, WI_I64X2_ADD); break;
+ case ONYX_INTRINSIC_I64X2_SUB: WI(call->token, WI_I64X2_SUB); break;
+ case ONYX_INTRINSIC_I64X2_MUL: WI(call->token, WI_I64X2_MUL); break;
+
+ case ONYX_INTRINSIC_F32X4_ABS: WI(call->token, WI_F32X4_ABS); break;
+ case ONYX_INTRINSIC_F32X4_NEG: WI(call->token, WI_F32X4_NEG); break;
+ case ONYX_INTRINSIC_F32X4_SQRT: WI(call->token, WI_F32X4_SQRT); break;
+ case ONYX_INTRINSIC_F32X4_ADD: WI(call->token, WI_F32X4_ADD); break;
+ case ONYX_INTRINSIC_F32X4_SUB: WI(call->token, WI_F32X4_SUB); break;
+ case ONYX_INTRINSIC_F32X4_MUL: WI(call->token, WI_F32X4_MUL); break;
+ case ONYX_INTRINSIC_F32X4_DIV: WI(call->token, WI_F32X4_DIV); break;
+ case ONYX_INTRINSIC_F32X4_MIN: WI(call->token, WI_F32X4_MIN); break;
+ case ONYX_INTRINSIC_F32X4_MAX: WI(call->token, WI_F32X4_MAX); break;
+
+ case ONYX_INTRINSIC_F64X2_ABS: WI(call->token, WI_F64X2_ABS); break;
+ case ONYX_INTRINSIC_F64X2_NEG: WI(call->token, WI_F64X2_NEG); break;
+ case ONYX_INTRINSIC_F64X2_SQRT: WI(call->token, WI_F64X2_SQRT); break;
+ case ONYX_INTRINSIC_F64X2_ADD: WI(call->token, WI_F64X2_ADD); break;
+ case ONYX_INTRINSIC_F64X2_SUB: WI(call->token, WI_F64X2_SUB); break;
+ case ONYX_INTRINSIC_F64X2_MUL: WI(call->token, WI_F64X2_MUL); break;
+ case ONYX_INTRINSIC_F64X2_DIV: WI(call->token, WI_F64X2_DIV); break;
+ case ONYX_INTRINSIC_F64X2_MIN: WI(call->token, WI_F64X2_MIN); break;
+ case ONYX_INTRINSIC_F64X2_MAX: WI(call->token, WI_F64X2_MAX); break;
+
+ case ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_S: WI(call->token, WI_I32X4_TRUNC_SAT_F32X4_S); break;
+ case ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_U: WI(call->token, WI_I32X4_TRUNC_SAT_F32X4_U); break;
+ case ONYX_INTRINSIC_F32X4_CONVERT_I32X4_S: WI(call->token, WI_F32X4_CONVERT_I32X4_S); break;
+ case ONYX_INTRINSIC_F32X4_CONVERT_I32X4_U: WI(call->token, WI_F32X4_CONVERT_I32X4_U); break;
+
+ case ONYX_INTRINSIC_ATOMIC_WAIT: {
+ Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+ emit_intrinsic_atomic_wait(mod, &code, atomic_type, call->token);
+ break;
+ }
+
+ case ONYX_INTRINSIC_ATOMIC_NOTIFY: {
+ emit_intrinsic_atomic_notify(mod, &code);
+ break;
+ }
+
+ case ONYX_INTRINSIC_ATOMIC_FENCE: {
+ emit_intrinsic_atomic_fence(mod, &code);
+ break;
+ }
+
+ case ONYX_INTRINSIC_ATOMIC_LOAD: {
+ Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+ emit_intrinsic_atomic_load(mod, &code, atomic_type, call->token);
+ break;
+ }
+
+ case ONYX_INTRINSIC_ATOMIC_STORE: {
+ Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+ emit_intrinsic_atomic_store(mod, &code, atomic_type, call->token);
+ break;
+ }
+
+ case ONYX_INTRINSIC_ATOMIC_ADD: {
+ Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+ emit_intrinsic_atomic_add(mod, &code, atomic_type, call->token);
+ break;
+ }
+
+ case ONYX_INTRINSIC_ATOMIC_SUB: {
+ Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+ emit_intrinsic_atomic_sub(mod, &code, atomic_type, call->token);
+ break;
+ }
+
+ case ONYX_INTRINSIC_ATOMIC_AND: {
+ Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+ emit_intrinsic_atomic_and(mod, &code, atomic_type, call->token);
+ break;
+ }
+
+ case ONYX_INTRINSIC_ATOMIC_OR: {
+ Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+ emit_intrinsic_atomic_or(mod, &code, atomic_type, call->token);
+ break;
+ }
+
+ case ONYX_INTRINSIC_ATOMIC_XOR: {
+ Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+ emit_intrinsic_atomic_xor(mod, &code, atomic_type, call->token);
+ break;
+ }
+
+ case ONYX_INTRINSIC_ATOMIC_XCHG: {
+ Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+ emit_intrinsic_atomic_xchg(mod, &code, atomic_type, call->token);
+ break;
+ }
+
+ case ONYX_INTRINSIC_ATOMIC_CMPXCHG: {
+ Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+ emit_intrinsic_atomic_cmpxchg(mod, &code, atomic_type, call->token);
+ 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(sub->token, WI_PTR_CONST, sub->elem_size);
+ WI(sub->token, 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(sub->token, 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;
+
+ if (memres->threadlocal) {
+ u64 tls_base_idx = bh_imap_get(&mod->index_map, (u64) &builtin_tls_base);
+ WID(NULL, WI_PTR_CONST, memres->tls_offset);
+ WIL(NULL, WI_GLOBAL_GET, tls_base_idx);
+ WI(NULL, WI_PTR_ADD);
+
+ } else {
+ // :ProperLinking
+ assert(memres->data_id != 0);
+ emit_data_relocation(mod, &code, memres->data_id);
+ }
+
+ *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(NULL, WI_LOCAL_GET, local_offset);
+
+ } else {
+ WIL(NULL, 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(NULL, WI_LOCAL_TEE, tmp_idx);
+
+ fori (i, 0, mem_count) {
+ type_linear_member_lookup(type, i, &two);
+ if (i != 0) WIL(NULL, 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(NULL, 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(NULL, WI_LOCAL_SET, temp_locals[i]);
+ }
+
+ if (!location_first) WIL(NULL, WI_LOCAL_SET, loc_idx);
+
+ fori (i, 0, elem_count) {
+ type_linear_member_lookup(type, i, &two);
+
+ u64 tmp_idx = temp_locals[i];
+ WIL(NULL, WI_LOCAL_GET, loc_idx);
+ WIL(NULL, 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(NULL, WI_LOCAL_SET, rptr_local);
+ WIL(NULL, WI_LOCAL_SET, lptr_local);
+
+ WIL(NULL, WI_LOCAL_GET, rptr_local);
+ WID(NULL, WI_I32_CONST, 0);
+ WI(NULL, WI_I32_NE);
+ emit_enter_structured_block(mod, &code, SBT_Basic_If, NULL);
+
+ //
+ // CLEANUP: Most of these cases could be much shorter if they used existing intrinsics.
+ //
+ 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(NULL, WI_LOCAL_GET, lptr_local);
+
+ WIL(NULL, 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(NULL, WI_LOCAL_GET, lptr_local);
+
+ if (offset != 0) {
+ WIL(NULL, WI_PTR_CONST, offset);
+ WI(NULL, WI_PTR_ADD);
+ }
+
+ WIL(NULL, WI_LOCAL_GET, rptr_local);
+ WIL(NULL, WI_I32_CONST, elem_count * elem_size);
+ WI(NULL, 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(NULL, WI_PTR_CONST, 0);
+ WIL(NULL, WI_LOCAL_SET, offset_local);
+
+ WID(NULL, WI_BLOCK_START, 0x40);
+ WID(NULL, WI_LOOP_START, 0x40);
+ WIL(NULL, WI_LOCAL_GET, offset_local);
+ WIL(NULL, WI_LOCAL_GET, lptr_local);
+ WI(NULL, WI_PTR_ADD);
+
+ WIL(NULL, WI_LOCAL_GET, offset_local);
+ WIL(NULL, WI_LOCAL_GET, rptr_local);
+ WI(NULL, WI_PTR_ADD);
+
+ emit_load_instruction(mod, &code, elem_type, 0);
+ emit_store_instruction(mod, &code, elem_type, offset);
+
+ WIL(NULL, WI_LOCAL_GET, offset_local);
+ WIL(NULL, WI_PTR_CONST, elem_size);
+ WI(NULL, WI_PTR_ADD);
+ WIL(NULL, WI_LOCAL_TEE, offset_local);
+
+ WIL(NULL, WI_PTR_CONST, elem_count * elem_size);
+ WI(NULL, WI_PTR_GE);
+ WID(NULL, WI_COND_JUMP, 0x01);
+
+ WID(NULL, WI_JUMP, 0x00);
+
+ WI(NULL, WI_LOOP_END);
+ WI(NULL, WI_BLOCK_END);
+
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+ }
+
+ WI(NULL, WI_ELSE);
+
+ { // If the source ptr is null (0), then just copy in 0 bytes.
+ WIL(NULL, WI_LOCAL_GET, lptr_local);
+ if (offset != 0) {
+ WIL(NULL, WI_PTR_CONST, offset);
+ WI(NULL, WI_PTR_ADD);
+ }
+
+ WIL(NULL, WI_I32_CONST, 0);
+
+ WIL(NULL, WI_I32_CONST, elem_count * elem_size);
+
+ if (context.options->use_post_mvp_features) {
+ WI(NULL, WI_MEMORY_FILL);
+ } else {
+ emit_intrinsic_memory_fill(mod, &code);
+ }
+ }
+
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+
+ emit_leave_structured_block(mod, &code);
+ *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(al->token, 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(al->token, WI_LOCAL_GET, mod->stack_base_idx);
+ WIL(al->token, WI_PTR_CONST, local_offset);
+ WI(al->token, 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_expr->token);
+ 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(if_expr->token, WI_LOCAL_SET, result_local);
+
+ offset = 0;
+ WI(if_expr->token, 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(if_expr->token, 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(if_expr->token, 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(doblock->block->token, 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;
+
+ expr = (AstTyped *) strip_aliases((AstNode *) expr);
+
+ switch (expr->kind) {
+ case Ast_Kind_Param:
+ case Ast_Kind_Local:
+ case Ast_Kind_Array_Literal:
+ case Ast_Kind_Struct_Literal: {
+ 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;
+ emit_memory_reservation_location(mod, &code, memres);
+ *offset_return = 0;
+ break;
+ }
+
+ default: {
+ onyx_report_error(expr->token->pos, Error_Critical, "Unable to generate location 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(NULL, WI_PTR_CONST, offset);
+ WI(NULL, 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->flags & Ast_Flag_Expr_Ignored) return;
+
+ if (type->type_id != 0) {
+ WID(NULL, WI_I32_CONST, ((AstType *) expr)->type_id);
+ } else {
+ Type* t = type_build_from_ast(context.ast_alloc, type);
+ WID(NULL, 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(NULL, WI_LOCAL_GET, localidx + idx);
+
+ } else {
+ assert(localidx & LOCAL_IS_WASM);
+ WIL(NULL, WI_LOCAL_GET, localidx);
+ }
+ break;
+ }
+
+ case Param_Pass_By_Implicit_Pointer: {
+ assert(localidx & LOCAL_IS_WASM);
+ WIL(NULL, 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(NULL, 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(NULL, WI_PTR_CONST, offset);
+ WI(NULL, WI_PTR_ADD);
+ }
+ }
+
+ break;
+ }
+
+ case Ast_Kind_Global: {
+ i32 globalidx = (i32) bh_imap_get(&mod->index_map, (u64) expr);
+
+ WID(NULL, 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;
+ }
+
+ WIR(NULL, instr);
+ break;
+ }
+
+ case Ast_Kind_StrLit: {
+ // :ProperLinking
+ AstStrLit *strlit = (AstStrLit *) expr;
+ assert(strlit->data_id > 0);
+ emit_data_relocation(mod, &code, strlit->data_id);
+
+ if (strlit->is_cstr == 0)
+ WID(NULL, WI_I32_CONST, strlit->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(NULL, 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;
+
+ if (node_is_addressable_literal((AstNode *) aof->expr)) {
+ aof->expr->flags |= Ast_Flag_Decl_Followed_By_Init;
+ aof->expr->flags |= Ast_Flag_Address_Taken;
+ emit_local_allocation(mod, &code, aof->expr);
+ emit_location(mod, &code, aof->expr);
+ emit_expression(mod, &code, aof->expr);
+ emit_store_instruction(mod, &code, aof->expr->type, 0);
+ }
+
+ 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;
+ assert(localidx & LOCAL_IS_WASM);
+ WIL(NULL, 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(NULL, 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(NULL, 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(NULL, WI_LOCAL_SET, temporaries[i]);
+ }
+
+ fori (i, 0, idx) WI(NULL, WI_DROP);
+
+ fori (i, 0, field_linear_members) {
+ type_linear_member_lookup(field->type, i, &two);
+
+ WIL(NULL, 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(NULL, WI_DROP);
+ WIL(NULL, WI_LOCAL_SET, hi_local);
+ WIL(NULL, WI_LOCAL_TEE, lo_local);
+ if (sl->elem_size != 1) {
+ WID(NULL, WI_I32_CONST, sl->elem_size);
+ WI(NULL, WI_I32_MUL);
+ }
+ emit_expression(mod, &code, sl->addr);
+ WI(NULL, WI_I32_ADD);
+ WIL(NULL, WI_LOCAL_GET, hi_local);
+ WIL(NULL, WI_LOCAL_GET, lo_local);
+ WI(NULL, 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(NULL, WI_I32_CONST, so->size);
+ break;
+ }
+
+ case Ast_Kind_Align_Of: {
+ AstAlignOf* ao = (AstAlignOf *) expr;
+ WID(NULL, WI_I32_CONST, ao->alignment);
+ break;
+ }
+
+ case Ast_Kind_Enum_Value: {
+ AstEnumValue* ev = (AstEnumValue *) expr;
+ AstNumLit * num = (AstNumLit *) ev->value;
+ assert(num->kind == Ast_Kind_NumLit);
+
+ WasmType backing_type = onyx_type_to_wasm_type(ev->type);
+ if (backing_type == WASM_TYPE_INT32) WID(NULL, WI_I32_CONST, num->value.i);
+ else if (backing_type == WASM_TYPE_INT64) WID(NULL, WI_I64_CONST, num->value.l);
+ else onyx_report_error(ev->token->pos, Error_Critical, "Invalid backing type for enum.");
+ break;
+ }
+
+ case Ast_Kind_Memres: {
+ AstMemRes* memres = (AstMemRes *) expr;
+ emit_memory_reservation_location(mod, &code, memres);
+ emit_load_instruction(mod, &code, memres->type, 0);
+ break;
+ }
+
+ case Ast_Kind_File_Contents: {
+ AstFileContents* fc = (AstFileContents *) expr;
+
+ assert(fc->data_id > 0);
+ assert(fc->size > 0);
+
+ // :ProperLinking
+ emit_data_relocation(mod, &code, fc->data_id);
+ WID(NULL, 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;
+ }
+
+ case Ast_Kind_Switch_Case: {
+ // This error message should be moved to checking, but this is the
+ // best place to do it right now.
+ onyx_report_error(expr->token->pos, Error_Critical, "'case' statements are only allowed in a 'switch' statement.");
+ break;
+ }
+
+ case Ast_Kind_Code_Block: {
+ // Like above, this error message should be moved to checking, but
+ // this is the best place to do it right now.
+ onyx_report_error(expr->token->pos, Error_Critical, "'#quote' blocks are only to be used at compile-time. Using them as a runtime value is not allowed.");
+ break;
+ }
+
+ case Ast_Kind_Foreign_Block: {
+ AstForeignBlock *fb = (AstForeignBlock *) expr;
+ WID(NULL, WI_I32_CONST, fb->foreign_block_number);
+ break;
+ }
+
+ case Ast_Kind_Zero_Value: {
+ AstZeroValue *zv = (AstZeroValue *) expr;
+ assert(zv->type);
+ emit_zero_value_for_type(mod, &code, zv->type, zv->token);
+ 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(NULL, 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_I32_FROM_I64, 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_I32_FROM_I64, 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(NULL, WI_DROP);
+ *pcode = code;
+ return;
+ }
+
+ if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
+ WID(NULL, WI_I32_CONST, from->Array.count);
+ *pcode = code;
+ return;
+ }
+
+ if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_DynArray) {
+ WI(NULL, WI_DROP);
+ WI(NULL, WI_DROP);
+ WI(NULL, WI_DROP);
+ *pcode = code;
+ return;
+ }
+
+ if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Slice) {
+ // Nothing needs to be done because they are identical
+ *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;
+ }
+
+ if (to->kind == Type_Kind_Distinct || from->kind == Type_Kind_Distinct) {
+ // 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(NULL, 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(NULL, WI_LOCAL_SET, dest_loc);
+
+ } else if (mod->curr_cc == CC_Return_Stack) {
+ WIL(NULL, WI_LOCAL_GET, mod->stack_base_idx);
+ WID(NULL, WI_I32_CONST, type_size_of(ret->expr->type));
+ WI(NULL, 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);
+
+ i64 jump_label = get_structured_jump_label(mod, Jump_Type_Return, 1);
+ if (jump_label >= 0) {
+ WIL(NULL, WI_JUMP, jump_label);
+
+ } else {
+ // 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--;
+ }
+ }
+
+ // Make a patch for the two instructions needed to restore the stack pointer
+ SUBMIT_PATCH(mod->stack_leave_patches, 0);
+ WI(NULL, WI_NOP);
+ WI(NULL, WI_NOP);
+
+ WI(NULL, 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(NULL, WI_I32_CONST, 0); break;
+ case WASM_TYPE_INT64: WIL(NULL, WI_I64_CONST, 0); break;
+ case WASM_TYPE_FLOAT32: WIL(NULL, WI_F32_CONST, 0); break;
+ case WASM_TYPE_FLOAT64: WIL(NULL, 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(NULL, 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(NULL, 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, Error_Critical, "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);
+ if (ft->Function.return_type->kind == Type_Kind_Struct && !type_is_compound(ft->Function.return_type)) {
+ return_type = onyx_type_to_wasm_type(ft->Function.return_type->Struct.linear_members[0].type);
+ }
+
+ *(t++) = (char) return_type;
+ *t = '\0';
+
+ i32 type_idx = 0;
+ i32 index = shgeti(mod->type_map, type_repr_buf);
+ if (index != -1) {
+ type_idx = mod->type_map[index].value;
+ } 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;
+
+ fori (i, 0, type->param_count) {
+ type->param_types[i] = type_repr_buf[i];
+ }
+
+ bh_arr_push(mod->types, type);
+
+ shput(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 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 = { 0 };
+ wasm_func.type_idx = type_idx;
+ wasm_func.location = fd->token;
+
+ bh_arr_new(mod->allocator, wasm_func.code, 16);
+
+ i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) fd);
+ mod->current_func_idx = func_idx;
+
+ debug_begin_function(mod, func_idx, fd->token, get_function_name(fd));
+
+ if (fd == builtin_initialize_data_segments && context.options->use_post_mvp_features) {
+ emit_initialize_data_segments_body(mod, &wasm_func.code);
+
+ debug_emit_instruction(mod, NULL);
+ 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);
+ mod->current_func_idx = -1;
+
+ debug_end_function(mod);
+ return;
+ }
+
+ if (fd == builtin_run_init_procedures) {
+ emit_run_init_procedures(mod, &wasm_func.code);
+
+ debug_emit_instruction(mod, NULL);
+ 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);
+ mod->current_func_idx = -1;
+
+ debug_end_function(mod);
+ return;
+ }
+
+ if (fd->body != NULL) {
+ mod->local_alloc = &wasm_func.locals;
+
+ // 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)) {
+ debug_introduce_symbol(mod, param->local->token, DSL_REGISTER, localidx | LOCAL_IS_WASM, 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: {
+ debug_introduce_symbol(mod, param->local->token, DSL_REGISTER, localidx | LOCAL_IS_WASM, param->local->type);
+ bh_imap_put(&mod->local_map, (u64) param->local, localidx++ | LOCAL_IS_WASM);
+ break;
+ }
+
+ default: assert(0);
+ }
+ }
+
+ 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);
+
+ debug_emit_instruction(mod, fd->token);
+ debug_emit_instruction(mod, fd->token);
+ debug_emit_instruction(mod, fd->token);
+ debug_emit_instruction(mod, fd->token);
+ debug_emit_instruction(mod, fd->token);
+ bh_arr_insert_end(wasm_func.code, 5);
+ fori (i, 0, 5) wasm_func.code[i] = (WasmInstruction) { WI_NOP, 0 };
+
+ // TODO: Emit debug info for the above instructions
+
+ 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.
+ debug_emit_instruction(mod, NULL);
+ debug_emit_instruction(mod, NULL);
+
+ 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);
+
+ if (fd->closing_brace) {
+ debug_emit_instruction(mod, fd->closing_brace);
+ bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_NOP, 0x00 }));
+ }
+
+ debug_emit_instruction(mod, NULL);
+ bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
+
+ bh_imap_clear(&mod->local_map);
+
+ bh_arr_set_at(mod->funcs, func_idx - mod->foreign_function_count, wasm_func);
+ mod->current_func_idx = -1;
+
+ debug_end_function(mod);
+}
+
+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 = bh_aprintf(global_heap_allocator, "%b", fd->foreign_module->text, fd->foreign_module->length),
+ .name = bh_aprintf(global_heap_allocator, "%b", fd->foreign_name->text, fd->foreign_name->length),
+ };
+
+ 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;
+ }
+
+ shput(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, glob);
+
+ if (global == &builtin_stack_top)
+ module->stack_top_ptr = &module->globals[global_idx].initial_value[0].data.i1;
+
+ if (global == &builtin_heap_start)
+ module->heap_start_ptr = &module->globals[global_idx].initial_value[0].data.i1;
+
+ if (global == &builtin_tls_size)
+ module->globals[global_idx].initial_value[0].data.i1 = module->next_tls_offset;
+}
+
+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);
+
+ i32 index = shgeti(mod->string_literals, (char *) strdata);
+ if (index != -1) {
+ StrLitInfo sti = mod->string_literals[index].value;
+ strlit->data_id = sti.data_id;
+ strlit->length = sti.len;
+
+ bh_free(global_heap_allocator, strdata);
+ return;
+ }
+
+ // :ProperLinking
+ u32 actual_length = length + (strlit->is_cstr ? 1 : 0);
+ WasmDatum datum = {
+ .alignment = 1,
+ .length = actual_length,
+ .data = strdata,
+ };
+
+ strlit->data_id = emit_data_entry(mod, &datum);
+ strlit->length = length;
+
+ // :ProperLinking
+ shput(mod->string_literals, (char *) strdata, ((StrLitInfo) { strlit->data_id, strlit->length }));
+}
+
+static u32 emit_data_entry(OnyxWasmModule *mod, WasmDatum *datum) {
+ datum->offset_ = 0;
+ datum->id = NEXT_DATA_ID(mod);
+ bh_arr_push(mod->data, *datum);
+ return datum->id;
+}
+
+static void emit_constexpr(ConstExprContext *ctx, AstTyped *node, u32 offset) {
+ if (!emit_constexpr_(ctx, node, offset)) {
+ onyx_report_error(node->token->pos, Error_Critical,
+ "Cannot generate constant data for '%s'.",
+ onyx_ast_node_kind_string(node->kind));
+ }
+}
+
+static b32 emit_constexpr_(ConstExprContext *ctx, AstTyped *node, u32 offset) {
+#define CE(type, off) (*((type *) bh_pointer_add(ctx->data, offset + (off))))
+ assert(ctx->data_id);
+ assert(ctx->data);
+
+ b32 retval = 1;
+ node = (AstTyped *) strip_aliases((AstNode *) node);
+
+ if (node_is_type((AstNode *) node)) {
+ Type* constructed_type = type_build_from_ast(context.ast_alloc, (AstType *) node);
+ CE(i32, 0) = constructed_type->id;
+ return 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_constexpr_(ctx, *expr, i * elem_size + offset);
+ 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_constexpr_(ctx, sl->args.values[i], smem.offset + offset);
+ }
+
+ break;
+ }
+
+ case Ast_Kind_StrLit: {
+ AstStrLit* sl = (AstStrLit *) node;
+
+ // NOTE: This assumes the data_id and the length fields have been filled out
+ // by emit_string_literal.
+ if (POINTER_SIZE == 4) {
+ CE(u32, 0) = 0;
+ CE(u32, 4) = sl->length;
+ } else {
+ CE(u64, 0) = 0;
+ CE(u64, 8) = sl->length;
+ }
+
+ assert(sl->data_id > 0);
+
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Data;
+ patch.index = ctx->data_id;
+ patch.location = offset;
+ patch.data_id = sl->data_id;
+ patch.offset = 0;
+ bh_arr_push(ctx->module->data_patches, patch);
+
+ break;
+ }
+
+ case Ast_Kind_Enum_Value: {
+ AstEnumValue* ev = (AstEnumValue *) node;
+ retval &= emit_constexpr_(ctx, (AstTyped *) ev->value, offset);
+ break;
+ }
+
+ case Ast_Kind_Function: {
+ AstFunction* func = (AstFunction *) node;
+ CE(u32, 0) = get_element_idx(ctx->module, func);
+ break;
+ }
+
+ case Ast_Kind_Size_Of: {
+ AstSizeOf* so = (AstSizeOf *) node;
+ CE(u32, 0) = so->size;
+ break;
+ }
+
+ case Ast_Kind_Align_Of: {
+ AstAlignOf* ao = (AstAlignOf *) node;
+ CE(u32, 0) = ao->alignment;
+ break;
+ }
+
+ case Ast_Kind_Zero_Value: {
+ memset(bh_pointer_add(ctx->data, offset), 0, type_size_of(node->type));
+ 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:
+ CE(i8, 0) = (i8) ((AstNumLit *) node)->value.i;
+ return retval;
+
+ case Basic_Kind_I16:
+ case Basic_Kind_U16:
+ CE(i16, 0) = (i16) ((AstNumLit *) node)->value.i;
+ return retval;
+
+ case Basic_Kind_I32:
+ case Basic_Kind_U32:
+ case Basic_Kind_Rawptr:
+ CE(i32, 0) = ((AstNumLit *) node)->value.i;
+ return retval;
+
+ case Basic_Kind_I64:
+ case Basic_Kind_U64:
+ CE(i64, 0) = ((AstNumLit *) node)->value.l;
+ return retval;
+
+ case Basic_Kind_F32:
+ CE(f32, 0) = ((AstNumLit *) node)->value.f;
+ return retval;
+
+ case Basic_Kind_F64:
+ CE(f64, 0) = ((AstNumLit *) node)->value.d;
+ return retval;
+
+ default: break;
+ }
+
+ //fallthrough
+ }
+
+ case Ast_Kind_Code_Block: break;
+
+ default: retval = 0;
+ }
+
+ return retval;
+
+#undef CE
+}
+
+static void emit_memory_reservation(OnyxWasmModule* mod, AstMemRes* memres) {
+ // :ProperLinking
+ 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->data_id = table_location;
+ return;
+ }
+
+ if (foreign_blocks_node != NULL && (AstMemRes *) foreign_blocks_node == memres) {
+ u64 foreign_blocks_location = build_foreign_blocks(mod);
+ memres->data_id = foreign_blocks_location;
+ return;
+ }
+
+ if (tagged_procedures_node != NULL && (AstMemRes *) tagged_procedures_node == memres) {
+ u64 tagged_procedures_location = build_tagged_procedures(mod);
+ memres->data_id = tagged_procedures_location;
+ return;
+ }
+
+ if (memres->threadlocal) {
+ memres->tls_offset = mod->next_tls_offset;
+ bh_align(memres->tls_offset, alignment);
+ mod->next_tls_offset = memres->tls_offset + size;
+
+ } else {
+ // :ProperLinking
+ u8* data = NULL;
+ if (memres->initial_value != NULL) {
+ assert(!memres->threadlocal);
+ data = bh_alloc(global_heap_allocator, size);
+ }
+
+ WasmDatum datum = {
+ .alignment = alignment,
+ .length = size,
+ .data = data,
+ };
+ memres->data_id = emit_data_entry(mod, &datum);
+
+ if (memres->initial_value != NULL) {
+ ConstExprContext constexpr_ctx;
+ constexpr_ctx.module = mod;
+ constexpr_ctx.data = data;
+ constexpr_ctx.data_id = memres->data_id;
+ emit_constexpr(&constexpr_ctx, memres->initial_value, 0);
+ }
+ }
+}
+
+static void emit_file_contents(OnyxWasmModule* mod, AstFileContents* fc) {
+ // 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);
+
+ OnyxToken *filename_token = fc->filename_expr->token;
+
+ token_toggle_end(filename_token);
+ char* temp_fn = bh_alloc_array(global_scratch_allocator, char, filename_token->length);
+ i32 temp_fn_len = string_process_escape_seqs(temp_fn, filename_token->text, filename_token->length);
+ char* filename = bh_lookup_file(temp_fn, parent_folder, "", 0, NULL, 0);
+ fc->filename = bh_strdup(global_heap_allocator, filename);
+ token_toggle_end(filename_token);
+ }
+
+ i32 index = shgeti(mod->loaded_file_info, fc->filename);
+ if (index != -1) {
+ StrLitInfo info = mod->loaded_file_info[index].value;
+ fc->data_id = info.data_id;
+ fc->size = info.len;
+ return;
+ }
+
+ if (!bh_file_exists(fc->filename)) {
+ onyx_report_error(fc->token->pos, Error_Critical,
+ "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);
+
+ WasmDatum datum = {
+ .alignment = 16,
+ .length = length,
+ .data = actual_data,
+ };
+ fc->data_id = emit_data_entry(mod, &datum);
+ fc->size = length - 1;
+
+ shput(mod->loaded_file_info, fc->filename, ((StrLitInfo) {
+ .data_id = fc->data_id,
+ .len = fc->size,
+ }));
+}
+
+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,
+ .data_patches = NULL,
+
+ .next_tls_offset = 0,
+ .tls_size_ptr = NULL,
+
+ .elems = NULL,
+ .next_elem_idx = 0,
+
+ .needs_memory_section = 0,
+ .memory_min_size = 0,
+ .memory_max_size = 0,
+
+ .structured_jump_target = NULL,
+ .return_location_stack = NULL,
+ .local_allocations = NULL,
+ .stack_leave_patches = NULL,
+ .deferred_stmts = NULL,
+
+ .heap_start_ptr = NULL,
+
+ .stack_top_ptr = NULL,
+ .stack_base_idx = 0,
+
+ .foreign_function_count = 0,
+
+ .null_proc_func_idx = -1,
+
+ .libraries = NULL,
+ .library_paths = NULL,
+
+ .foreign_blocks = NULL,
+ .next_foreign_block_idx = 0,
+
+ .procedures_with_tags = NULL
+ };
+
+ 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(alloc, module.libraries, 4);
+ bh_arr_new(alloc, module.library_paths, 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);
+
+ sh_new_arena(module.type_map);
+ sh_new_arena(module.exports);
+ sh_new_arena(module.loaded_file_info);
+ sh_new_arena(module.string_literals);
+
+ 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);
+ bh_arr_new(global_heap_allocator, module.foreign_blocks, 4);
+ bh_arr_new(global_heap_allocator, module.procedures_with_tags, 4);
+ bh_arr_new(global_heap_allocator, module.data_patches, 4);
+
+#ifdef ENABLE_DEBUG_INFO
+ module.debug_context = bh_alloc_item(context.ast_alloc, DebugContext);
+ module.debug_context->allocator = global_heap_allocator;
+ module.debug_context->next_file_id = 0;
+ module.debug_context->next_sym_id = 0;
+ module.debug_context->last_token = NULL;
+
+ sh_new_arena(module.debug_context->file_info);
+ bh_arr_new(global_heap_allocator, module.debug_context->sym_info, 32);
+ bh_arr_new(global_heap_allocator, module.debug_context->sym_patches, 32);
+ bh_arr_new(global_heap_allocator, module.debug_context->funcs, 16);
+
+ bh_buffer_init(&module.debug_context->op_buffer, global_heap_allocator, 1024);
+#endif
+
+ return module;
+}
+
+void emit_entity(Entity* ent) {
+ OnyxWasmModule* module = context.wasm_module;
+ module->current_func_idx = -1;
+
+ 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);
+ }
+
+ if (ent->function->tags != NULL) {
+ bh_arr_push(module->procedures_with_tags, ent->function);
+ }
+ break;
+
+ 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);
+ }
+
+ if (ent->expr->kind == Ast_Kind_Directive_Library) {
+ bh_arr_push(module->libraries, ent->library->library_name);
+ }
+ break;
+ }
+
+ case Entity_Type_Foreign_Block: {
+ ent->foreign_block->foreign_block_number = module->next_foreign_block_idx++;
+ bh_arr_push(module->foreign_blocks, (AstForeignBlock *) ent->foreign_block);
+ 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_link(OnyxWasmModule *module, OnyxWasmLinkOptions *options) {
+ // If the pointer size is going to change,
+ // the code will probably need to be altered.
+ assert(POINTER_SIZE == 4);
+
+ module->memory_min_size = options->memory_min_size;
+ module->memory_max_size = options->memory_max_size;
+
+ if (context.options->use_multi_threading || options->import_memory) {
+ module->needs_memory_section = 0;
+
+ WasmImport mem_import = {
+ .kind = WASM_FOREIGN_MEMORY,
+ .min = options->memory_min_size,
+ .max = options->memory_max_size, // NOTE: Why not use all 4 Gigs of memory?
+ .shared = context.options->runtime == Runtime_Js,
+
+ .mod = options->import_memory_module_name,
+ .name = options->import_memory_import_name,
+ };
+
+ bh_arr_push(module->imports, mem_import);
+
+ } else {
+ module->needs_memory_section = 1;
+ }
+
+ if (options->export_memory) {
+ WasmExport mem_export = {
+ .kind = WASM_FOREIGN_MEMORY,
+ .idx = 0,
+ };
+
+ shput(module->exports, options->export_memory_name, mem_export);
+ module->export_count++;
+ }
+
+ if (options->export_func_table) {
+ WasmExport func_table_export = {
+ .kind = WASM_FOREIGN_TABLE,
+ .idx = 0,
+ };
+
+ shput(module->exports, options->export_func_table_name, func_table_export);
+ module->export_count++;
+ }
+
+ u32 datum_offset = options->null_reserve_size;
+ bh_arr_each(WasmDatum, datum, module->data) {
+ assert(datum->id > 0);
+
+ bh_align(datum_offset, datum->alignment);
+ datum->offset_ = datum_offset;
+
+ datum_offset += datum->length;
+ }
+
+ bh_arr_each(DatumPatchInfo, patch, module->data_patches) {
+ assert(patch->data_id > 0);
+ WasmDatum *datum = &module->data[patch->data_id - 1];
+ assert(datum->id == patch->data_id);
+
+ switch (patch->kind) {
+ case Datum_Patch_Instruction: {
+ WasmFunc *func = &module->funcs[patch->index - module->foreign_function_count];
+
+ assert(func->code[patch->location].type == WI_PTR_CONST);
+ func->code[patch->location].data.l = (u64) datum->offset_ + patch->offset;
+ break;
+ }
+
+ case Datum_Patch_Data: {
+ WasmDatum *datum_to_alter = &module->data[patch->index - 1];
+ assert(datum_to_alter->id == patch->index);
+
+ *((u32 *) bh_pointer_add(datum_to_alter->data, patch->location)) = (u32) datum->offset_ + patch->offset;
+ break;
+ }
+
+ case Datum_Patch_Relative: {
+ WasmDatum *datum_to_alter = &module->data[patch->index - 1];
+ assert(datum_to_alter->id == patch->index);
+
+ u32 *addr = (u32 *) bh_pointer_add(datum_to_alter->data, patch->location);
+ if (*addr != 0) {
+ *addr += (u32) datum->offset_ + patch->offset;
+ }
+
+ break;
+ }
+
+ default: assert(0);
+ }
+ }
+
+ assert(module->stack_top_ptr && module->heap_start_ptr);
+
+ *module->stack_top_ptr = datum_offset;
+ bh_align(*module->stack_top_ptr, options->stack_alignment);
+
+ *module->heap_start_ptr = *module->stack_top_ptr + options->stack_size;
+ bh_align(*module->heap_start_ptr, 16);
+}
+
+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);
+ shfree(module->type_map);
+ shfree(module->exports);
+}
+
+
+b32 onyx_wasm_build_link_options_from_node(OnyxWasmLinkOptions *opts, AstTyped *node) {
+ node = (AstTyped *) strip_aliases((AstNode *) node);
+
+ assert(node && node->kind == Ast_Kind_Struct_Literal);
+ assert(builtin_link_options_type);
+
+ Type *link_options_type = type_build_from_ast(context.ast_alloc, builtin_link_options_type);
+
+ AstStructLiteral *input = (AstStructLiteral *) node;
+
+ StructMember smem;
+ b32 out_is_valid;
+
+ // TODO: These should be properly error handled.
+ assert(type_lookup_member(link_options_type, "stack_first", &smem));
+ opts->stack_first = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid) != 0;
+ if (!out_is_valid) return 0;
+
+ assert(type_lookup_member(link_options_type, "stack_size", &smem));
+ opts->stack_size = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
+ if (!out_is_valid) return 0;
+
+ assert(type_lookup_member(link_options_type, "stack_alignment", &smem));
+ opts->stack_alignment = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
+ if (!out_is_valid) return 0;
+
+ assert(type_lookup_member(link_options_type, "null_reserve_size", &smem));
+ opts->null_reserve_size = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
+ if (!out_is_valid) return 0;
+
+ assert(type_lookup_member(link_options_type, "import_memory", &smem));
+ opts->import_memory = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid) != 0;
+ if (!out_is_valid) return 0;
+
+ assert(type_lookup_member(link_options_type, "import_memory_module_name", &smem));
+ opts->import_memory_module_name = get_expression_string_value(input->args.values[smem.idx], &out_is_valid);
+ if (!out_is_valid) return 0;
+
+ assert(type_lookup_member(link_options_type, "import_memory_import_name", &smem));
+ opts->import_memory_import_name = get_expression_string_value(input->args.values[smem.idx], &out_is_valid);
+ if (!out_is_valid) return 0;
+
+ assert(type_lookup_member(link_options_type, "export_memory", &smem));
+ opts->export_memory = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid) != 0;
+ if (!out_is_valid) return 0;
+
+ assert(type_lookup_member(link_options_type, "export_memory_name", &smem));
+ opts->export_memory_name = get_expression_string_value(input->args.values[smem.idx], &out_is_valid);
+ if (!out_is_valid) return 0;
+
+ assert(type_lookup_member(link_options_type, "export_func_table", &smem));
+ opts->export_func_table = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid) != 0;
+ if (!out_is_valid) return 0;
+
+ assert(type_lookup_member(link_options_type, "export_func_table_name", &smem));
+ opts->export_func_table_name = get_expression_string_value(input->args.values[smem.idx], &out_is_valid);
+ if (!out_is_valid) return 0;
+
+ assert(type_lookup_member(link_options_type, "memory_min_size", &smem));
+ opts->memory_min_size = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
+ if (!out_is_valid) return 0;
+
+ assert(type_lookup_member(link_options_type, "memory_max_size", &smem));
+ opts->memory_max_size = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
+ if (!out_is_valid) return 0;
+
+ return 1;
+}
+
+#include "wasm_output.h"
--- /dev/null
+// This file is directly included in src/onxywasm.c
+// It is here purely to decrease the amount of clutter in the main file.
+
+
+// IMPROVE: This implementation assumes that the source and destination buffers do not overlap.
+// The specification for memory.copy in WASM does work even if the buffers overlap.
+// Also, this implementation copies byte-by-byte, which is terrible. It should copy
+// quad word by quad word, and then the additional bytes if the count was not divisible by 8.
+// :32BitPointers
+EMIT_FUNC_NO_ARGS(intrinsic_memory_copy) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ // The stack should look like this:
+ // <count>
+ // <source>
+ // <dest>
+
+ u64 count_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+ u64 source_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+ u64 dest_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+
+ WIL(NULL, WI_LOCAL_SET, count_local);
+ WIL(NULL, WI_LOCAL_SET, source_local);
+ WIL(NULL, WI_LOCAL_SET, dest_local);
+
+ // count is greater than 0
+ WIL(NULL, WI_LOCAL_GET, count_local);
+ WID(NULL, WI_I32_CONST, 0);
+ WI(NULL, WI_I32_GT_S);
+
+ WID(NULL, WI_IF_START, 0x40);
+ WID(NULL, WI_LOOP_START, 0x40);
+
+ WIL(NULL, WI_LOCAL_GET, count_local);
+ WID(NULL, WI_I32_CONST, 1);
+ WI(NULL, WI_I32_SUB);
+ WIL(NULL, WI_LOCAL_SET, count_local);
+
+ WIL(NULL, WI_LOCAL_GET, dest_local);
+ WIL(NULL, WI_LOCAL_GET, count_local);
+ WI(NULL, WI_PTR_ADD);
+
+ WIL(NULL, WI_LOCAL_GET, source_local);
+ WIL(NULL, WI_LOCAL_GET, count_local);
+ WI(NULL, WI_PTR_ADD);
+
+ WID(NULL, WI_I32_LOAD_8_U, ((WasmInstructionData) { 0, 0 }));
+ WID(NULL, WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
+
+ WIL(NULL, WI_LOCAL_GET, count_local);
+ WID(NULL, WI_I32_CONST, 0);
+ WI(NULL, WI_I32_GT_S);
+ WID(NULL, WI_COND_JUMP, 0x00);
+
+ WI(NULL, WI_LOOP_END);
+ WI(NULL, WI_IF_END);
+
+ local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+
+ *pcode = code;
+}
+
+EMIT_FUNC_NO_ARGS(intrinsic_memory_fill) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ // The stack should look like this:
+ // <count>
+ // <byte>
+ // <dest>
+
+ u64 count_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+ u64 byte_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+ u64 dest_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+
+ WIL(NULL, WI_LOCAL_SET, count_local);
+ WIL(NULL, WI_LOCAL_SET, byte_local);
+ WIL(NULL, WI_LOCAL_SET, dest_local);
+
+ // count is greater than 0
+ WIL(NULL, WI_LOCAL_GET, count_local);
+ WID(NULL, WI_I32_CONST, 0);
+ WI(NULL, WI_I32_GT_S);
+
+ WID(NULL, WI_IF_START, 0x40);
+ WID(NULL, WI_LOOP_START, 0x40);
+
+ WIL(NULL, WI_LOCAL_GET, count_local);
+ WID(NULL, WI_I32_CONST, 1);
+ WI(NULL, WI_I32_SUB);
+ WIL(NULL, WI_LOCAL_SET, count_local);
+
+ WIL(NULL, WI_LOCAL_GET, dest_local);
+ WIL(NULL, WI_LOCAL_GET, count_local);
+ WI(NULL, WI_PTR_ADD);
+
+ WIL(NULL, WI_LOCAL_GET, byte_local);
+ WID(NULL, WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
+
+ WIL(NULL, WI_LOCAL_GET, count_local);
+ WID(NULL, WI_I32_CONST, 0);
+ WI(NULL, WI_I32_GT_S);
+ WID(NULL, WI_COND_JUMP, 0x00);
+
+ WI(NULL, WI_LOOP_END);
+ WI(NULL, 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(NULL, 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(NULL, 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, Error_Critical,
+ "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;
+}
+
+EMIT_FUNC(intrinsic_atomic_wait, Type* type, OnyxToken* where) {
+ if (type->kind != Type_Kind_Basic) goto bad_type;
+
+ bh_arr(WasmInstruction) code = *pcode;
+
+ switch (type->Basic.kind) {
+ case Basic_Kind_I32:
+ case Basic_Kind_U32: WID(NULL, WI_ATOMIC_WAIT32, ((WasmInstructionData) { 2, 0 })); break;
+
+ case Basic_Kind_I64:
+ case Basic_Kind_U64: WID(NULL, WI_ATOMIC_WAIT64, ((WasmInstructionData) { 3, 0 })); break;
+
+ default: goto bad_type;
+ }
+
+ *pcode = code;
+ return;
+
+bad_type:
+ onyx_report_error(where->pos, Error_Critical, "Bad type for atomic wait, '%s'. Only i32 and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC_NO_ARGS(intrinsic_atomic_notify) {
+ bh_arr(WasmInstruction) code = *pcode;
+ WID(NULL, WI_ATOMIC_NOTIFY, ((WasmInstructionData) { 2, 0 }));
+ *pcode = code;
+}
+
+EMIT_FUNC_NO_ARGS(intrinsic_atomic_fence) {
+ bh_arr(WasmInstruction) code = *pcode;
+ WI(NULL, WI_ATOMIC_FENCE);
+ *pcode = code;
+}
+
+EMIT_FUNC(intrinsic_atomic_load, Type* type, OnyxToken* where) {
+ if (type->kind != Type_Kind_Basic) goto bad_type;
+
+ bh_arr(WasmInstruction) code = *pcode;
+
+ switch (type->Basic.kind) {
+ case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_LOAD8_U, ((WasmInstructionData) { 0, 0 })); break;
+ case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_LOAD16_U, ((WasmInstructionData) { 1, 0 })); break;
+
+ case Basic_Kind_I32:
+ case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_LOAD, ((WasmInstructionData) { 2, 0 })); break;
+
+ case Basic_Kind_I64:
+ case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_LOAD, ((WasmInstructionData) { 3, 0 })); break;
+
+ default: goto bad_type;
+ }
+
+ *pcode = code;
+ return;
+
+bad_type:
+ onyx_report_error(where->pos, Error_Critical, "Bad type for atomic load, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC(intrinsic_atomic_store, Type* type, OnyxToken* where) {
+ if (type->kind != Type_Kind_Basic) goto bad_type;
+
+ bh_arr(WasmInstruction) code = *pcode;
+
+ switch (type->Basic.kind) {
+ case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_STORE8, ((WasmInstructionData) { 0, 0 })); break;
+ case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_STORE16, ((WasmInstructionData) { 1, 0 })); break;
+
+ case Basic_Kind_I32:
+ case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_STORE, ((WasmInstructionData) { 2, 0 })); break;
+
+ case Basic_Kind_I64:
+ case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_STORE, ((WasmInstructionData) { 3, 0 })); break;
+
+ default: goto bad_type;
+ }
+
+ *pcode = code;
+ return;
+
+bad_type:
+ onyx_report_error(where->pos, Error_Critical, "Bad type for atomic store, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC(intrinsic_atomic_add, Type* type, OnyxToken* where) {
+ if (type->kind != Type_Kind_Basic) goto bad_type;
+
+ bh_arr(WasmInstruction) code = *pcode;
+
+ switch (type->Basic.kind) {
+ case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_ADD8_U, ((WasmInstructionData) { 0, 0 })); break;
+ case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_ADD16_U, ((WasmInstructionData) { 1, 0 })); break;
+
+ case Basic_Kind_I32:
+ case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_ADD, ((WasmInstructionData) { 2, 0 })); break;
+
+ case Basic_Kind_I64:
+ case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_ADD, ((WasmInstructionData) { 3, 0 })); break;
+
+ default: goto bad_type;
+ }
+
+ *pcode = code;
+ return;
+
+bad_type:
+ onyx_report_error(where->pos, Error_Critical, "Bad type for atomic add, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC(intrinsic_atomic_sub, Type* type, OnyxToken* where) {
+ if (type->kind != Type_Kind_Basic) goto bad_type;
+
+ bh_arr(WasmInstruction) code = *pcode;
+
+ switch (type->Basic.kind) {
+ case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_SUB8_U, ((WasmInstructionData) { 0, 0 })); break;
+ case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_SUB16_U, ((WasmInstructionData) { 1, 0 })); break;
+
+ case Basic_Kind_I32:
+ case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_SUB, ((WasmInstructionData) { 2, 0 })); break;
+
+ case Basic_Kind_I64:
+ case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_SUB, ((WasmInstructionData) { 3, 0 })); break;
+
+ default: goto bad_type;
+ }
+
+ *pcode = code;
+ return;
+
+bad_type:
+ onyx_report_error(where->pos, Error_Critical, "Bad type for atomic sub, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC(intrinsic_atomic_and, Type* type, OnyxToken* where) {
+ if (type->kind != Type_Kind_Basic) goto bad_type;
+
+ bh_arr(WasmInstruction) code = *pcode;
+
+ switch (type->Basic.kind) {
+ case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_AND8_U, ((WasmInstructionData) { 0, 0 })); break;
+ case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_AND16_U, ((WasmInstructionData) { 1, 0 })); break;
+
+ case Basic_Kind_I32:
+ case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_AND, ((WasmInstructionData) { 2, 0 })); break;
+
+ case Basic_Kind_I64:
+ case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_AND, ((WasmInstructionData) { 3, 0 })); break;
+
+ default: goto bad_type;
+ }
+
+ *pcode = code;
+ return;
+
+bad_type:
+ onyx_report_error(where->pos, Error_Critical, "Bad type for atomic and, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC(intrinsic_atomic_or, Type* type, OnyxToken* where) {
+ if (type->kind != Type_Kind_Basic) goto bad_type;
+
+ bh_arr(WasmInstruction) code = *pcode;
+
+ switch (type->Basic.kind) {
+ case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_OR8_U, ((WasmInstructionData) { 0, 0 })); break;
+ case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_OR16_U, ((WasmInstructionData) { 1, 0 })); break;
+
+ case Basic_Kind_I32:
+ case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_OR, ((WasmInstructionData) { 2, 0 })); break;
+
+ case Basic_Kind_I64:
+ case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_OR, ((WasmInstructionData) { 3, 0 })); break;
+
+ default: goto bad_type;
+ }
+
+ *pcode = code;
+ return;
+
+bad_type:
+ onyx_report_error(where->pos, Error_Critical, "Bad type for atomic or, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC(intrinsic_atomic_xor, Type* type, OnyxToken* where) {
+ if (type->kind != Type_Kind_Basic) goto bad_type;
+
+ bh_arr(WasmInstruction) code = *pcode;
+
+ switch (type->Basic.kind) {
+ case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_XOR8_U, ((WasmInstructionData) { 0, 0 })); break;
+ case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_XOR16_U, ((WasmInstructionData) { 1, 0 })); break;
+
+ case Basic_Kind_I32:
+ case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_XOR, ((WasmInstructionData) { 2, 0 })); break;
+
+ case Basic_Kind_I64:
+ case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_XOR, ((WasmInstructionData) { 3, 0 })); break;
+
+ default: goto bad_type;
+ }
+
+ *pcode = code;
+ return;
+
+bad_type:
+ onyx_report_error(where->pos, Error_Critical, "Bad type for atomic xor, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC(intrinsic_atomic_xchg, Type* type, OnyxToken* where) {
+ if (type->kind != Type_Kind_Basic) goto bad_type;
+
+ bh_arr(WasmInstruction) code = *pcode;
+
+ switch (type->Basic.kind) {
+ case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_XCHG8_U, ((WasmInstructionData) { 0, 0 })); break;
+ case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_XCHG16_U, ((WasmInstructionData) { 1, 0 })); break;
+
+ case Basic_Kind_I32:
+ case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_XCHG, ((WasmInstructionData) { 2, 0 })); break;
+
+ case Basic_Kind_I64:
+ case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_XCHG, ((WasmInstructionData) { 3, 0 })); break;
+
+ default: goto bad_type;
+ }
+
+ *pcode = code;
+ return;
+
+bad_type:
+ onyx_report_error(where->pos, Error_Critical, "Bad type for atomic xchg, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC(intrinsic_atomic_cmpxchg, Type* type, OnyxToken* where) {
+ if (type->kind != Type_Kind_Basic) goto bad_type;
+
+ bh_arr(WasmInstruction) code = *pcode;
+
+ switch (type->Basic.kind) {
+ case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_CMPXCHG8_U, ((WasmInstructionData) { 0, 0 })); break;
+ case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_CMPXCHG16_U, ((WasmInstructionData) { 1, 0 })); break;
+
+ case Basic_Kind_I32:
+ case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_CMPXCHG, ((WasmInstructionData) { 2, 0 })); break;
+
+ case Basic_Kind_I64:
+ case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_CMPXCHG, ((WasmInstructionData) { 3, 0 })); break;
+
+ default: goto bad_type;
+ }
+
+ *pcode = code;
+ return;
+
+bad_type:
+ onyx_report_error(where->pos, Error_Critical, "Bad type for atomic cmpxchg, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
+}
+
+EMIT_FUNC_NO_ARGS(initialize_data_segments_body) {
+ // :ProperLinking
+ if (!context.options->use_multi_threading || !context.options->use_post_mvp_features) return;
+
+ bh_arr(WasmInstruction) code = *pcode;
+
+ //
+ // Because this code is generated direction in the function
+ // it is assumed that EVERY data entry will be entered by
+ // this point. If data section entries can be entered after
+ // function body generation starts, this code will have to
+ // move to a link phase thing.
+ i32 index = 0;
+ bh_arr_each(WasmDatum, datum, mod->data) {
+ assert(datum->id > 0);
+ if (datum->data == NULL) { index++; continue; }
+
+ emit_data_relocation(mod, &code, datum->id);
+ WID(NULL, WI_PTR_CONST, 0);
+ WID(NULL, WI_I32_CONST, datum->length);
+ WID(NULL, WI_MEMORY_INIT, ((WasmInstructionData) { index, 0 }));
+
+ index += 1;
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC_NO_ARGS(run_init_procedures) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ bh_arr_each(AstFunction *, func, init_procedures) {
+ i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) *func);
+ debug_emit_instruction(mod, NULL);
+ bh_arr_push(code, ((WasmInstruction){ WI_CALL, func_idx }));
+ }
+
+ *pcode = code;
+}
--- /dev/null
+// This file is included in src/onyxwasm.c.
+// It is separated because of its fundamentally different goals.
+
+//-------------------------------------------------
+// BINARY OUPUT
+//-------------------------------------------------
+
+#define WASM_SECTION_ID_CUSTOM 0
+#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_DATACOUNT 12
+#define WASM_SECTION_ID_CODE 10
+#define WASM_SECTION_ID_DATA 11
+
+typedef i32 vector_func(void*, bh_buffer*);
+
+static const u8 ONYX_MAGIC_STRING[] = "ONYX";
+static const u8 WASM_MAGIC_STRING[] = { 0x00, 0x61, 0x73, 0x6D };
+static const u8 WASM_VERSION[] = { 0x01, 0x00, 0x00, 0x00 };
+
+static inline i32 output_unsigned_integer(u64 i, bh_buffer *buff) {
+ i32 leb_len;
+ u8* leb = uint_to_uleb128(i, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ return leb_len;
+}
+
+static inline i32 output_custom_section_name(char *name, bh_buffer *buff) {
+ u32 len = strlen(name);
+ i32 len_len = output_unsigned_integer(len, buff);
+ bh_buffer_append(buff, name, len);
+ return len_len + len;
+}
+
+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, b32 shared, bh_buffer* buff) {
+ i32 leb_len, prev_len = buff->length;
+ u8* leb;
+
+ u8 mem_type = 0x00;
+ if (max >= 0) mem_type |= 0x01;
+ if (shared) mem_type |= 0x02;
+
+ bh_buffer_write_byte(buff, mem_type);
+
+ 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, 0, &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) {
+ // :ProperLinking
+ // if (context.options->use_multi_threading) return 0;
+ if (!module->needs_memory_section) return 0;
+
+ 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);
+
+ output_limits(module->memory_min_size, -1, 0, &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, strlen(import->mod), &vec_buff);
+ output_name(import->name, strlen(import->name), &vec_buff);
+ bh_buffer_write_byte(&vec_buff, (u8) import->kind);
+
+ switch (import->kind) {
+ case WASM_FOREIGN_FUNCTION:
+ leb = uint_to_uleb128((u64) import->idx, &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+ break;
+
+ case WASM_FOREIGN_MEMORY:
+ output_limits(import->min, import->max, import->shared, &vec_buff);
+ break;
+
+ case WASM_FOREIGN_TABLE: assert(0);
+ }
+ }
+
+ 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;
+ fori (i, 0, shlen(module->exports)) {
+ char *key = module->exports[i].key;
+ WasmExport value = module->exports[i].value;
+
+ 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);
+ }
+
+ 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;
+ fori (i, 0, shlen(module->exports)) {
+ char *key = module->exports[i].key;
+ WasmExport value = module->exports[i].value;
+
+ if (value.kind == WASM_FOREIGN_FUNCTION) {
+ if (strncmp("main", key, 5) == 0) {
+ start_idx = value.idx;
+ break;
+ }
+ }
+ }
+
+ 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_UNREACHABLE) assert(("EMITTING UNREACHABLE!!", 0));
+
+ if (instr->type == WI_NOP && !context.options->debug_enabled) 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 if (instr->type & ATOMIC_INSTR_MASK) {
+ bh_buffer_write_byte(buff, 0xFE);
+ leb = uint_to_uleb128((u64) (instr->type &~ ATOMIC_INSTR_MASK), &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ if (instr->type == WI_ATOMIC_FENCE) {
+ bh_buffer_write_byte(buff, 0x00);
+
+ } else {
+ 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);
+ }
+
+ } 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_INIT:
+ 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);
+
+ assert(func->code);
+
+ // 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);
+
+ 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_datacountsection(OnyxWasmModule* module, bh_buffer* buff) {
+ if (!context.options->use_post_mvp_features) return 0;
+
+ i32 prev_len = buff->length;
+
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_DATACOUNT);
+
+ 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);
+
+ 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) {
+ i32 memory_flags = 0x00;
+ // :ProperLinking
+ if (context.options->use_multi_threading) memory_flags |= 0x01;
+
+ bh_buffer_write_byte(&vec_buff, memory_flags);
+
+ // :ProperLinking
+ if (!context.options->use_multi_threading) {
+ 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);
+ }
+
+ if (datum->data != NULL) {
+ 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]);
+ } else {
+ leb = uint_to_uleb128(0, &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_onyx_libraries_section(OnyxWasmModule* module, bh_buffer* buff) {
+ if (bh_arr_length(module->libraries) == 0) return 0;
+ i32 prev_len = buff->length;
+
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+ bh_buffer libs_buff;
+ bh_buffer_init(&libs_buff, buff->allocator, 128);
+
+ output_custom_section_name("_onyx_libs", &libs_buff);
+
+ output_unsigned_integer(bh_arr_length(module->library_paths), &libs_buff);
+
+ bh_arr_each(char *, lib, module->library_paths) {
+ assert(*lib != NULL);
+
+ u32 lib_len = strlen(*lib);
+ output_unsigned_integer(lib_len, &libs_buff);
+ bh_buffer_append(&libs_buff, *lib, lib_len);
+ }
+
+ output_unsigned_integer(bh_arr_length(module->libraries), &libs_buff);
+
+ bh_arr_each(char *, lib, module->libraries) {
+ assert(*lib != NULL);
+
+ u32 lib_len = strlen(*lib);
+ output_unsigned_integer(lib_len, &libs_buff);
+ bh_buffer_append(&libs_buff, *lib, lib_len);
+ }
+
+ output_unsigned_integer(libs_buff.length, buff);
+
+ bh_buffer_concat(buff, libs_buff);
+ bh_buffer_free(&libs_buff);
+
+ return buff->length - prev_len;
+}
+
+static i32 output_onyx_func_offset_section(OnyxWasmModule* module, bh_buffer* buff) {
+ i32 prev_len = buff->length;
+
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+ bh_buffer section_buff;
+ bh_buffer_init(§ion_buff, buff->allocator, 128);
+
+ output_custom_section_name("_onyx_func_offsets", §ion_buff);
+
+ i32 func_count = bh_arr_length(module->funcs) + module->foreign_function_count;
+
+ bh_buffer name_buff;
+ bh_buffer_init(&name_buff, buff->allocator, 1024);
+ u32 str_cursor = func_count * 4;
+ fori (i, 0, func_count) {
+ bh_buffer_write_u32(§ion_buff, str_cursor);
+
+ if (i < module->foreign_function_count) {
+ bh_buffer_append(&name_buff, "<imported function>", 20);
+ str_cursor += 20;
+ } else {
+ WasmFunc *func = &module->funcs[i - module->foreign_function_count];
+ assert(func->location);
+ char *str = bh_bprintf("%s:%d,%d\0", func->location->pos.filename, func->location->pos.line, func->location->pos.column);
+ i32 len = strlen(str);
+ bh_buffer_append(&name_buff, str, len + 1);
+ str_cursor += len + 1;
+ }
+ }
+
+ bh_buffer_concat(§ion_buff, name_buff);
+
+ output_unsigned_integer(section_buff.length, buff);
+
+ bh_buffer_concat(buff, section_buff);
+ bh_buffer_free(§ion_buff);
+
+ return buff->length - prev_len;
+}
+
+#ifdef ENABLE_DEBUG_INFO
+static i32 output_ovm_debug_sections(OnyxWasmModule* module, bh_buffer* buff) {
+ if (!module->debug_context || !context.options->debug_enabled) return 0;
+
+ DebugContext *ctx = module->debug_context;
+
+ bh_buffer section_buff;
+ bh_buffer_init(§ion_buff, buff->allocator, 128);
+
+ {
+ // ovm_debug_files section
+ bh_buffer_clear(§ion_buff);
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+ output_custom_section_name("ovm_debug_files", §ion_buff);
+
+ i32 file_count = shlenu(ctx->file_info);
+ output_unsigned_integer(file_count, §ion_buff);
+
+ fori (i, 0, file_count) {
+ Table(DebugFileInfo) entry = (void *) &ctx->file_info[i];
+ output_unsigned_integer(entry->value.file_id, §ion_buff);
+ output_unsigned_integer(entry->value.line_count, §ion_buff);
+ output_name(entry->key, strlen(entry->key), §ion_buff);
+ }
+
+ output_unsigned_integer(section_buff.length, buff);
+
+ bh_buffer_concat(buff, section_buff);
+ }
+
+ {
+ // ovm_debug_funcs section
+ bh_buffer_clear(§ion_buff);
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+ output_custom_section_name("ovm_debug_funcs", §ion_buff);
+
+ i32 func_count = bh_arr_length(ctx->funcs);
+ output_unsigned_integer(func_count, §ion_buff);
+
+ fori (i, 0, func_count) {
+ DebugFuncContext *func = &ctx->funcs[i];
+ output_unsigned_integer(func->func_index, §ion_buff);
+ output_unsigned_integer(func->file_id, §ion_buff);
+ output_unsigned_integer(func->line, §ion_buff);
+ output_name(func->name, func->name_length, §ion_buff);
+ output_unsigned_integer(1, §ion_buff);
+ output_unsigned_integer(func->op_offset, §ion_buff);
+
+ LocalAllocator *locals = &module->funcs[i].locals;
+ if (func->stack_ptr_idx > 0) {
+ u32 local_idx = local_lookup_idx(locals, func->stack_ptr_idx);
+ output_unsigned_integer(local_idx, §ion_buff);
+ } else {
+ output_unsigned_integer(0, §ion_buff);
+ }
+
+ output_unsigned_integer(0, §ion_buff);
+ }
+
+ output_unsigned_integer(section_buff.length, buff);
+
+ bh_buffer_concat(buff, section_buff);
+ }
+
+ {
+ // ovm_debug_syms section
+
+ // First, apply patches for register locations
+ bh_arr_each(DebugSymPatch, patch, ctx->sym_patches) {
+ // CLEANUP: This is (kind of) incorrect, as there is nothing guarenteeing
+ // that the symbol with id a will be a position a, other than the way
+ // that this has been implemented right now.
+ assert(ctx->sym_info[patch->sym_id].location_type == DSL_REGISTER);
+
+ LocalAllocator *locals = &module->funcs[patch->func_idx - module->foreign_function_count].locals;
+ ctx->sym_info[patch->sym_id].location_num = local_lookup_idx(locals, patch->local_idx);
+ }
+
+ bh_buffer_clear(§ion_buff);
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+ output_custom_section_name("ovm_debug_syms", §ion_buff);
+
+ i32 sym_count = bh_arr_length(ctx->sym_info);
+ output_unsigned_integer(sym_count, §ion_buff);
+
+ fori (i, 0, sym_count) {
+ DebugSymInfo *sym = &ctx->sym_info[i];
+ output_unsigned_integer(sym->sym_id, §ion_buff);
+ if (sym->name) {
+ output_name(sym->name, strlen(sym->name), §ion_buff);
+ } else {
+ output_unsigned_integer(0, §ion_buff);
+ }
+ output_unsigned_integer(sym->location_type, §ion_buff);
+ output_unsigned_integer(sym->location_num, §ion_buff);
+ output_unsigned_integer(sym->type, §ion_buff);
+ }
+
+ output_unsigned_integer(section_buff.length, buff);
+
+ bh_buffer_concat(buff, section_buff);
+ }
+
+ {
+ // ovm_debug_types section
+ bh_buffer_clear(§ion_buff);
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+ output_custom_section_name("ovm_debug_types", §ion_buff);
+
+ i32 type_count = bh_arr_length(type_map.entries);
+ output_unsigned_integer(type_count, §ion_buff);
+
+ bh_arr_each(bh__imap_entry, entry, type_map.entries) {
+ u32 id = entry->key;
+ Type *type = (Type *) entry->value;
+ const char *name = type_get_name(type);
+
+ output_unsigned_integer(id, §ion_buff);
+ output_name(name, strlen(name), §ion_buff);
+ output_unsigned_integer(type_size_of(type), §ion_buff);
+
+ if (type->kind == Type_Kind_Basic) {
+ // Type indicies are special because they are encoded
+ // as a "distinct" unsigned 32-bit integer, which is
+ // effectively how they are used in the code anyway.
+ //
+ // This is probably a change that will be made throughout
+ // the entire compiler, but for now they will remain as
+ // a special type.
+ if (type->Basic.kind == Basic_Kind_Type_Index) {
+ output_unsigned_integer(5, §ion_buff);
+ output_unsigned_integer(2, §ion_buff);
+ output_unsigned_integer(basic_types[Basic_Kind_U32].id, §ion_buff);
+ continue;
+ }
+
+ if (type->Basic.kind == Basic_Kind_Rawptr) {
+ // rawptr -> ^void
+ output_unsigned_integer(2, §ion_buff);
+ output_unsigned_integer(1, §ion_buff);
+ output_unsigned_integer(basic_types[Basic_Kind_Void].id, §ion_buff);
+ continue;
+ }
+
+ output_unsigned_integer(1, §ion_buff);
+ if (type->Basic.kind == Basic_Kind_Void) output_unsigned_integer(0, §ion_buff);
+ else if (type_is_bool(type)) output_unsigned_integer(4, §ion_buff);
+ else if (type_is_integer(type)) {
+ if (type->Basic.flags & Basic_Flag_Unsigned) output_unsigned_integer(2, §ion_buff);
+ else output_unsigned_integer(1, §ion_buff);
+ }
+ else if (type->Basic.flags & Basic_Flag_Float) output_unsigned_integer(3, §ion_buff);
+ else if (type_is_simd(type)) output_unsigned_integer(6, §ion_buff);
+ else {
+ output_unsigned_integer(0, §ion_buff);
+ }
+
+ continue;
+ }
+
+ if (type->kind == Type_Kind_Pointer) {
+ output_unsigned_integer(2, §ion_buff);
+ output_unsigned_integer(1, §ion_buff);
+ output_unsigned_integer(type->Pointer.elem->id, §ion_buff);
+ continue;
+ }
+
+ if (type->kind == Type_Kind_Enum) {
+ output_unsigned_integer(5, §ion_buff);
+ output_unsigned_integer(2, §ion_buff);
+ output_unsigned_integer(type->Enum.backing->id, §ion_buff);
+ continue;
+ }
+
+ if (type->kind == Type_Kind_Array) {
+ output_unsigned_integer(4, §ion_buff);
+ output_unsigned_integer(type->Array.count, §ion_buff);
+ output_unsigned_integer(type->Array.elem->id, §ion_buff);
+ continue;
+ }
+
+ if (type_is_structlike_strict(type)) {
+ output_unsigned_integer(3, §ion_buff);
+
+ i32 mem_count = type_structlike_mem_count(type);
+ output_unsigned_integer(mem_count, §ion_buff);
+
+ fori (i, 0, mem_count) {
+ StructMember smem;
+ type_lookup_member_by_idx(type, i, &smem);
+
+ output_unsigned_integer(smem.offset, §ion_buff);
+ output_unsigned_integer(smem.type->id, §ion_buff);
+ output_name(smem.name, strlen(smem.name), §ion_buff);
+ }
+
+ continue;
+ }
+
+ if (type->kind == Type_Kind_Function) {
+ output_unsigned_integer(6, §ion_buff);
+ output_unsigned_integer(type->Function.param_count, §ion_buff);
+
+ fori (i, 0, (i32) type->Function.param_count) {
+ output_unsigned_integer(type->Function.params[i]->id, §ion_buff);
+ }
+
+ output_unsigned_integer(type->Function.return_type->id, §ion_buff);
+ continue;
+ }
+
+ if (type->kind == Type_Kind_Distinct) {
+ output_unsigned_integer(5, §ion_buff);
+ output_unsigned_integer(2, §ion_buff);
+ output_unsigned_integer(type->Distinct.base_type->id, §ion_buff);
+ continue;
+ }
+
+ // No debug information will be given about the poly struct
+ // or compound types.
+ // Outside of runtime type information, they provide no useful
+ // debugging information (I don't think at least...).
+ if (type->kind == Type_Kind_PolyStruct ||
+ type->kind == Type_Kind_Compound) {
+ output_unsigned_integer(1, §ion_buff);
+ output_unsigned_integer(0, §ion_buff);
+ continue;
+ }
+
+ assert(("Unhandled type", 0));
+ }
+
+ output_unsigned_integer(section_buff.length, buff);
+
+ bh_buffer_concat(buff, section_buff);
+ }
+
+ {
+ // ovm_debug_ops section
+ bh_buffer_clear(§ion_buff);
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+ output_custom_section_name("ovm_debug_ops", §ion_buff);
+ bh_buffer_concat(§ion_buff, ctx->op_buffer);
+
+ output_unsigned_integer(section_buff.length, buff);
+
+ bh_buffer_concat(buff, section_buff);
+ }
+
+ bh_buffer_free(§ion_buff);
+ return 0;
+}
+#endif
+
+void onyx_wasm_module_write_to_buffer(OnyxWasmModule* module, bh_buffer* buffer) {
+ bh_buffer_init(buffer, global_heap_allocator, 128);
+ if (context.options->runtime == Runtime_Onyx) {
+ bh_buffer_append(buffer, ONYX_MAGIC_STRING, 4);
+ } else {
+ bh_buffer_append(buffer, WASM_MAGIC_STRING, 4);
+ }
+ bh_buffer_append(buffer, WASM_VERSION, 4);
+
+#ifdef ENABLE_DEBUG_INFO
+ output_ovm_debug_sections(module, buffer);
+#endif
+ output_typesection(module, buffer);
+ output_importsection(module, buffer);
+ output_funcsection(module, buffer);
+ output_tablesection(module, buffer);
+ output_memorysection(module, buffer);
+ output_globalsection(module, buffer);
+ output_exportsection(module, buffer);
+ output_startsection(module, buffer);
+ output_elemsection(module, buffer);
+ output_datacountsection(module, buffer);
+ output_codesection(module, buffer);
+ output_datasection(module, buffer);
+ output_onyx_libraries_section(module, buffer);
+
+ // TODO: Consider if this should always be included?
+ // It can amount to a lot of extra data.
+ output_onyx_func_offset_section(module, buffer);
+}
+
+void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file) {
+ bh_buffer master_buffer;
+ onyx_wasm_module_write_to_buffer(module, &master_buffer);
+
+ bh_file_write(&file, master_buffer.data, master_buffer.length);
+}
--- /dev/null
+#include "bh.h"
+#include "utils.h"
+#include "astnodes.h"
+#include "wasm.h"
+#include "onyx_library.h"
+
+#ifndef USE_OVM_DEBUGGER
+ #include "wasmer.h"
+#endif
+
+#ifdef _BH_LINUX
+ #include <pthread.h>
+ #include <signal.h>
+ #include <sys/wait.h>
+ #include <dlfcn.h>
+#endif
+
+static wasm_config_t* wasm_config;
+static wasm_engine_t* wasm_engine;
+static wasm_store_t* wasm_store;
+static wasm_extern_vec_t wasm_imports;
+static bh_buffer wasm_raw_bytes;
+wasm_instance_t* wasm_instance;
+wasm_module_t* wasm_module;
+wasm_memory_t* wasm_memory;
+
+OnyxRuntime wasm_runtime;
+
+b32 wasm_name_equals(const wasm_name_t* name1, const wasm_name_t* name2) {
+ if (name1->size != name2->size) return 0;
+ return !strncmp(name1->data, name2->data, name1->size);
+}
+
+b32 wasm_name_equals_string(const wasm_name_t* name1, const char* name2) {
+ u32 name2_size = strlen(name2);
+ if (name1->size != name2_size) return 0;
+ return !strncmp(name1->data, name2, name1->size);
+}
+
+wasm_extern_t* wasm_extern_lookup_by_name(wasm_module_t* module, wasm_instance_t* instance, const char* name) {
+ i32 name_len = strlen(name);
+
+ i32 idx = -1;
+ wasm_exporttype_vec_t export_types;
+ wasm_module_exports(module, &export_types);
+ fori (i, 0, (i64) export_types.size) {
+ wasm_exporttype_t* export_type = export_types.data[i];
+ const wasm_name_t* export_name = wasm_exporttype_name(export_type);
+
+ if (!strncmp(export_name->data, name, name_len)) {
+ idx = i;
+ break;
+ }
+ }
+
+ if (idx == -1) return NULL;
+
+ wasm_extern_vec_t exports;
+ wasm_instance_exports(instance, &exports);
+
+ return exports.data[idx];
+}
+
+
+
+typedef void *(*LibraryLinker)(OnyxRuntime *runtime);
+
+typedef struct LibraryLinkContext {
+ bh_buffer wasm_bytes;
+ bh_arr(char *) library_paths;
+} LibraryLinkContext;
+
+static WasmFuncDefinition** onyx_load_library(LibraryLinkContext *ctx, char *name) {
+ #ifdef _BH_LINUX
+ #define DIR_SEPARATOR '/'
+ #endif
+ #ifdef _BH_WINDOWS
+ #define DIR_SEPARATOR '\\'
+ #endif
+
+ char *library = name;
+ fori (i, 0, (i32) strlen(name)) {
+ if (name[i] == DIR_SEPARATOR) library = &name[i + 1];
+ }
+
+ char *library_load_name_tmp = bh_bprintf("onyx_library_%s", library);
+ char *library_load_name = alloca(strlen(library_load_name_tmp) + 1);
+ strcpy(library_load_name, library_load_name_tmp);
+ LibraryLinker library_load;
+
+ #ifdef _BH_LINUX
+ char *library_name = bh_lookup_file(name, ".", ".so", 1, (const char **) ctx->library_paths, 1);
+ void* handle = dlopen(library_name, RTLD_LAZY);
+ if (handle == NULL) {
+ printf("ERROR LOADING LIBRARY %s: %s\n", name, dlerror());
+ return NULL;
+ }
+
+ library_load = (LibraryLinker) dlsym(handle, library_load_name);
+ if (library_load == NULL) {
+ printf("ERROR RESOLVING '%s': %s\n", library_load_name, dlerror());
+ return NULL;
+ }
+ #endif
+
+ #ifdef _BH_WINDOWS
+ char *library_name = bh_lookup_file(name, ".", ".dll", 1, (const char **) ctx->library_paths, 1);
+ HMODULE handle = LoadLibraryA(library_name);
+ if (handle == NULL) {
+ printf("ERROR LOADING LIBRARY %s: %d\n", name, GetLastError());
+ return NULL;
+ }
+
+ library_load = (LibraryLinker) GetProcAddress(handle, library_load_name);
+ if (library_load == NULL) {
+ printf("ERROR RESOLVING '%s': %d\n", library_load_name, GetLastError());
+ return NULL;
+ }
+ #endif
+
+ return library_load(runtime);
+}
+
+static void lookup_and_load_custom_libraries(LibraryLinkContext *ctx, bh_arr(WasmFuncDefinition **)* p_out) {
+ bh_arr(WasmFuncDefinition **) out = *p_out;
+
+ bh_buffer wasm_bytes = ctx->wasm_bytes;
+
+ i32 cursor = 8; // skip the magic number and version
+ while (cursor < wasm_bytes.length) {
+ u64 section_number = uleb128_to_uint(wasm_bytes.data, &cursor);
+ u64 section_size = uleb128_to_uint(wasm_bytes.data, &cursor);
+
+ i32 section_start = cursor;
+ if (section_number == 0) {
+ u64 name_len = uleb128_to_uint(wasm_bytes.data, &cursor);
+ if (!strncmp(wasm_bytes.data + cursor, "_onyx_libs", name_len)) {
+ cursor += name_len;
+ u64 lib_count = uleb128_to_uint(wasm_bytes.data, &cursor);
+
+ fori (i, 0, (i64) lib_count) {
+ u64 lib_path_length = uleb128_to_uint(wasm_bytes.data, &cursor);
+ lib_path_length = bh_min(lib_path_length, 512);
+
+ char *lib_path = malloc(lib_path_length);
+ strncpy(lib_path, wasm_bytes.data + cursor, lib_path_length);
+ lib_path[lib_path_length] = '\0';
+ bh_path_convert_separators(lib_path);
+ cursor += lib_path_length;
+
+ bh_arr_push(ctx->library_paths, lib_path);
+ }
+
+ lib_count = uleb128_to_uint(wasm_bytes.data, &cursor);
+
+ fori (i, 0, (i64) lib_count) {
+ u64 lib_name_length = uleb128_to_uint(wasm_bytes.data, &cursor);
+ lib_name_length = bh_min(lib_name_length, 256);
+
+ char library_name[256];
+ strncpy(library_name, wasm_bytes.data + cursor, lib_name_length);
+ library_name[lib_name_length] = '\0';
+ cursor += lib_name_length;
+
+ WasmFuncDefinition** lib = onyx_load_library(ctx, library_name);
+ if (lib) {
+ bh_arr_push(out, lib);
+ }
+ }
+ break;
+ }
+ }
+
+ cursor = section_start + section_size;
+ }
+
+ *p_out = out;
+}
+
+static void onyx_print_trap(wasm_trap_t* trap) {
+ wasm_message_t msg;
+ wasm_trap_message(trap, &msg);
+ bh_printf("TRAP: %b\n", msg.data, msg.size);
+
+ i32 func_name_section = 0;
+
+ i32 cursor = 8; // skip the magic number and version
+ while (cursor < wasm_raw_bytes.length) {
+ u64 section_number = uleb128_to_uint(wasm_raw_bytes.data, &cursor);
+ u64 section_size = uleb128_to_uint(wasm_raw_bytes.data, &cursor);
+
+ i32 section_start = cursor;
+ if (section_number == 0) {
+ u64 name_len = uleb128_to_uint(wasm_raw_bytes.data, &cursor);
+ if (!strncmp(wasm_raw_bytes.data + cursor, "_onyx_func_offsets", name_len)) {
+ cursor += name_len;
+ func_name_section = cursor;
+ break;
+ }
+ }
+
+ cursor = section_start + section_size;
+ }
+
+ if (func_name_section == 0) return;
+
+ bh_printf("TRACE:\n");
+ wasm_frame_vec_t frames;
+ wasm_trap_trace(trap, &frames);
+ fori (i, 0, (i32) frames.size) {
+ i32 func_idx = wasm_frame_func_index(frames.data[i]);
+ i32 mod_offset = wasm_frame_module_offset(frames.data[i]);
+
+ i32 cursor = func_name_section + 4 * func_idx;
+ i32 func_offset = *(i32 *) (wasm_raw_bytes.data + cursor);
+ char* func_name = wasm_raw_bytes.data + func_name_section + func_offset;
+
+ bh_printf(" func[%d]:%p at %s\n", func_idx, mod_offset, func_name);
+ }
+}
+
+static void cleanup_wasm_objects() {
+ if (wasm_instance) wasm_instance_delete(wasm_instance);
+ if (wasm_module) wasm_module_delete(wasm_module);
+ if (wasm_store) wasm_store_delete(wasm_store);
+ if (wasm_engine) wasm_engine_delete(wasm_engine);
+}
+
+//
+// This could be cleaned up a bit, as this function directly modifies various global variables.
+// Those being wasm_memory and wasm_imports.
+static b32 link_wasm_imports(bh_arr(WasmFuncDefinition **) linkable_functions, wasm_module_t *wasm_module) {
+ wasm_importtype_vec_t module_imports;
+ wasm_module_imports(wasm_module, &module_imports);
+
+ wasm_extern_vec_new_uninitialized(&wasm_imports, module_imports.size); // @Free
+
+ fori (i, 0, (i32) module_imports.size) {
+ const wasm_name_t* module_name = wasm_importtype_module(module_imports.data[i]);
+ const wasm_name_t* import_name = wasm_importtype_name(module_imports.data[i]);
+
+ wasm_extern_t* import = NULL;
+
+ if (wasm_name_equals_string(module_name, "onyx")) {
+ if (wasm_name_equals_string(import_name, "memory")) {
+ if (wasm_memory == NULL) {
+ wasm_limits_t limits = { 1024, 65536 };
+ wasm_memorytype_t* memory_type = wasm_memorytype_new(&limits);
+ wasm_memory = wasm_memory_new(wasm_store, memory_type);
+ }
+
+ import = wasm_memory_as_extern(wasm_memory);
+ goto import_found;
+ }
+ }
+
+ bh_arr_each(WasmFuncDefinition **, library_funcs, linkable_functions) {
+ WasmFuncDefinition **pcurrent_function = *library_funcs;
+ while (*pcurrent_function != NULL) {
+ WasmFuncDefinition *cf = *pcurrent_function;
+ if (wasm_name_equals_string(module_name, cf->module_name) && wasm_name_equals_string(import_name, cf->import_name)) {
+ wasm_valtype_vec_t wasm_params;
+ wasm_valtype_vec_new_uninitialized(&wasm_params, cf->params->count);
+ fori (k, 0, cf->params->count) wasm_params.data[k] = wasm_valtype_new(cf->params->types[k]);
+
+ wasm_valtype_vec_t wasm_results;
+ wasm_valtype_vec_new_uninitialized(&wasm_results, cf->results->count);
+ fori (k, 0, cf->results->count) wasm_results.data[k] = wasm_valtype_new(cf->results->types[k]);
+
+ wasm_functype_t* wasm_functype = wasm_functype_new(&wasm_params, &wasm_results);
+
+ wasm_func_t* wasm_func = wasm_func_new(wasm_store, wasm_functype, cf->func);
+ import = wasm_func_as_extern(wasm_func);
+ goto import_found;
+ }
+
+ pcurrent_function += 1;
+ }
+ }
+
+ goto bad_import;
+
+ import_found:
+ wasm_imports.data[i] = import;
+ continue;
+
+
+ bad_import:
+ bh_printf("Couldn't find import %b.%b.\n", module_name->data, module_name->size, import_name->data, import_name->size);
+ return 0;
+ }
+
+ return 1;
+}
+
+void onyx_run_initialize(b32 debug_enabled) {
+ wasm_config = wasm_config_new();
+ if (!wasm_config) {
+ cleanup_wasm_objects();
+ return;
+ }
+
+#ifdef USE_OVM_DEBUGGER
+ void wasm_config_enable_debug(wasm_config_t *config, int value);
+ wasm_config_enable_debug(wasm_config, debug_enabled);
+#endif
+
+#ifndef USE_OVM_DEBUGGER
+ if (debug_enabled) {
+ printf("Warning: --debug does nothing if libovmwasm.so is not being used!\n");
+ }
+
+ // Prefer the LLVM compile because it is faster. This should be configurable from the command line and/or a top-level directive.
+ if (wasmer_is_compiler_available(LLVM)) {
+ wasm_config_set_compiler(wasm_config, LLVM);
+ }
+
+ wasmer_features_t* features = wasmer_features_new();
+ wasmer_features_simd(features, 1);
+ wasmer_features_threads(features, 1);
+ wasmer_features_bulk_memory(features, 1);
+ wasm_config_set_features(wasm_config, features);
+#endif
+
+ wasm_engine = wasm_engine_new_with_config(wasm_config);
+ if (!wasm_engine) {
+ cleanup_wasm_objects();
+ return;
+ }
+
+ wasm_store = wasm_store_new(wasm_engine);
+ if (!wasm_store) {
+ cleanup_wasm_objects();
+ return;
+ }
+
+ // See comment in onyx_library.h about us being the linker.
+ wasm_runtime.wasm_memory_data = &wasm_memory_data;
+ wasm_runtime.wasm_extern_lookup_by_name = &wasm_extern_lookup_by_name;
+ wasm_runtime.wasm_extern_as_func = &wasm_extern_as_func;
+ wasm_runtime.wasm_func_call = &wasm_func_call;
+ wasm_runtime.wasm_instance_new = &wasm_instance_new;
+ wasm_runtime.wasm_store_new = &wasm_store_new;
+ wasm_runtime.wasm_store_delete = &wasm_store_delete;
+ wasm_runtime.onyx_print_trap = &onyx_print_trap;
+}
+
+b32 onyx_run_wasm(bh_buffer wasm_bytes, int argc, char *argv[]) {
+ runtime = &wasm_runtime;
+
+ if (strncmp(wasm_bytes.data, "ONYX", 4)) {
+ printf("Bad magic bytes for Onyx binary.\n");
+ return 0;
+ }
+
+ wasm_bytes.data[0] = '\0';
+ wasm_bytes.data[1] = 'a';
+ wasm_bytes.data[2] = 's';
+ wasm_bytes.data[3] = 'm';
+ wasm_raw_bytes = wasm_bytes;
+
+ bh_arr(WasmFuncDefinition **) linkable_functions = NULL;
+ bh_arr_new(bh_heap_allocator(), linkable_functions, 4);
+
+ {
+ LibraryLinkContext lib_ctx;
+ lib_ctx.wasm_bytes = wasm_bytes;
+ lib_ctx.library_paths = NULL;
+ lookup_and_load_custom_libraries(&lib_ctx, &linkable_functions);
+
+ bh_arr_free(lib_ctx.library_paths);
+ }
+
+ wasm_byte_vec_t wasm_data;
+ wasm_data.size = wasm_bytes.length;
+ wasm_data.data = wasm_bytes.data;
+
+ wasm_module = wasm_module_new(wasm_store, &wasm_data);
+ if (!wasm_module) {
+ cleanup_wasm_objects();
+ return 0;
+ }
+
+ wasm_imports = (wasm_extern_vec_t) WASM_EMPTY_VEC;
+ if (!link_wasm_imports(linkable_functions, wasm_module)) {
+ return 0;
+ }
+
+ wasm_trap_t* traps = NULL;
+
+ wasm_instance = wasm_instance_new(wasm_store, wasm_module, &wasm_imports, &traps);
+ if (!wasm_instance) {
+ cleanup_wasm_objects();
+ return 0;
+ }
+
+ wasm_runtime.wasm_engine = wasm_engine;
+ wasm_runtime.wasm_module = wasm_module;
+ wasm_runtime.wasm_imports = wasm_imports;
+ wasm_runtime.wasm_memory = wasm_memory;
+ wasm_runtime.wasm_instance = wasm_instance;
+
+ wasm_runtime.argc = argc;
+ wasm_runtime.argv = argv;
+
+ wasm_extern_t* start_extern = wasm_extern_lookup_by_name(wasm_module, wasm_instance, "_start");
+ wasm_func_t* start_func = wasm_extern_as_func(start_extern);
+
+ wasm_val_vec_t args;
+ wasm_val_vec_t results;
+ wasm_val_vec_new_uninitialized(&args, 0);
+ wasm_val_vec_new_uninitialized(&results, 1);
+
+ wasm_trap_t *run_trap = wasm_func_call(start_func, &args, &results);
+
+#if 1
+ if (run_trap != NULL) onyx_print_trap(run_trap);
+#endif
+
+ goto end;
+
+ bh_printf("An error occured trying to run the WASM module...\n");
+
+#ifndef USE_OVM_DEBUGGER
+ i32 len = wasmer_last_error_length();
+ char *buf = alloca(len + 1);
+ wasmer_last_error_message(buf, len);
+ bh_printf("%b\n", buf, len);
+#endif
+
+end:
+ cleanup_wasm_objects();
+ return run_trap == NULL;
+}
--- /dev/null
+// This file is directly included in src/onxywasm.c
+// It is here purely to decrease the amount of clutter in the main file.
+
+typedef struct StructMethodData {
+ u32 name_loc;
+ u32 name_len;
+ u32 type;
+ u32 data_loc;
+} StructMethodData;
+
+static 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))
+#define WRITE_PTR(val) \
+ bh_buffer_align(&table_buffer, POINTER_SIZE); \
+ PATCH; \
+ if (POINTER_SIZE == 4) bh_buffer_write_u32(&table_buffer, val); \
+ if (POINTER_SIZE == 8) bh_buffer_write_u64(&table_buffer, val);
+#define WRITE_SLICE(ptr, count) \
+ WRITE_PTR(ptr); \
+ if (POINTER_SIZE == 4) bh_buffer_write_u32(&table_buffer, count); \
+ if (POINTER_SIZE == 8) bh_buffer_write_u64(&table_buffer, count);
+
+ // This is the data behind the "type_table" slice in runtime/info/types.onyx
+ #if (POINTER_SIZE == 4)
+ #define Table_Info_Type u32
+ #else
+ #define Table_Info_Type u64
+ #endif
+ u32 type_count = bh_arr_length(type_map.entries) + 1;
+ Table_Info_Type* table_info = bh_alloc_array(global_heap_allocator, Table_Info_Type, type_count); // HACK
+ memset(table_info, 0, type_count * sizeof(Table_Info_Type));
+
+ bh_buffer table_buffer;
+ bh_buffer_init(&table_buffer, global_heap_allocator, 4096);
+
+ u32 type_table_info_data_id = NEXT_DATA_ID(module);
+
+ ConstExprContext constexpr_ctx;
+ constexpr_ctx.module = module;
+ constexpr_ctx.data_id = type_table_info_data_id;
+
+ // 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.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.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.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));
+ WRITE_SLICE(components_base, 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);
+
+ WRITE_SLICE(parameters_base, 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);
+ WRITE_SLICE(name_loc, (*value)->token->length);
+
+ assert((*value)->value->kind == Ast_Kind_NumLit);
+ AstNumLit *num = (AstNumLit *) (*value)->value;
+ bh_buffer_write_u64(&table_buffer, num->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);
+ WRITE_SLICE(name_base, name_length);
+ WRITE_SLICE(member_base, 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);
+ u32* meta_locations = bh_alloc_array(global_scratch_allocator, u32, s->mem_count);
+ u32* struct_tag_locations = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(s->meta_tags));
+ memset(value_locations, 0, s->mem_count * sizeof(u32));
+ memset(meta_locations, 0, s->mem_count * sizeof(u32));
+ memset(struct_tag_locations, 0, bh_arr_length(s->meta_tags) * sizeof(u32));
+
+ // Member names
+ 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);
+
+ // Polymorphic solutions
+ 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);
+ constexpr_ctx.data = table_buffer.data;
+ if (emit_constexpr_(&constexpr_ctx, sln->value, table_buffer.length)) {
+ table_buffer.length += size;
+ break;
+ }
+
+ // fallthrough
+ }
+
+ default: {
+ // Set to null if this is not known how to encode
+ param_locations[i-1] = 0;
+ break;
+ }
+ }
+ }
+
+ bh_buffer_align(&table_buffer, 8);
+
+ // Member default values
+ 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);
+ constexpr_ctx.data = table_buffer.data;
+ if (!emit_constexpr_(&constexpr_ctx, value, table_buffer.length)) {
+ // 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;
+ }
+ }
+
+ // Member tags
+ i = 0;
+ bh_arr_each(StructMember*, pmem, s->memarr) {
+ StructMember* mem = *pmem;
+
+ if (mem->meta_tags == NULL) {
+ i += 1;
+ continue;
+ }
+
+ bh_arr(AstTyped *) meta_tags = mem->meta_tags;
+ assert(meta_tags);
+
+ bh_arr(u64) meta_tag_locations=NULL;
+ bh_arr_new(global_heap_allocator, meta_tag_locations, bh_arr_length(meta_tags));
+
+ int j = 0;
+ bh_arr_each(AstTyped *, meta, meta_tags) {
+ AstTyped* value = *meta;
+ assert(value->flags & Ast_Flag_Comptime);
+ assert(value->type);
+
+ u32 size = type_size_of(value->type);
+ bh_buffer_align(&table_buffer, type_alignment_of(value->type));
+ meta_tag_locations[j] = table_buffer.length;
+
+ bh_buffer_grow(&table_buffer, table_buffer.length + size);
+ constexpr_ctx.data = table_buffer.data;
+ assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
+ table_buffer.length += size;
+
+ j += 1;
+ }
+
+ bh_buffer_align(&table_buffer, 8);
+ meta_locations[i] = table_buffer.length;
+
+ fori (k, 0, bh_arr_length(meta_tags)) {
+ WRITE_SLICE(meta_tag_locations[k], meta_tags[k]->type->id);
+ }
+
+ bh_arr_free(meta_tag_locations);
+ i += 1;
+ }
+
+ bh_buffer_align(&table_buffer, 8);
+ u32 members_base = table_buffer.length;
+
+ // Member array
+ i = 0;
+ bh_arr_each(StructMember*, pmem, s->memarr) {
+ StructMember* mem = *pmem;
+
+ u32 name_loc = name_locations[i];
+ u32 value_loc = value_locations[i];
+ u32 meta_loc = meta_locations[i++];
+
+ WRITE_SLICE(name_loc, 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);
+
+ WRITE_PTR(value_loc);
+
+ WRITE_SLICE(meta_loc, bh_arr_length(mem->meta_tags));
+ }
+
+ bh_buffer_align(&table_buffer, 8);
+ u32 params_base = table_buffer.length;
+
+ // Polymorphic solution any array
+ i = 0;
+ bh_arr_each(AstPolySolution, sln, s->poly_sln) {
+ WRITE_PTR(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);
+ }
+
+ // Struct tag array
+ i = 0;
+ bh_arr_each(AstTyped *, tag, s->meta_tags) {
+ AstTyped* value = *tag;
+ assert(value->flags & Ast_Flag_Comptime);
+ assert(value->type);
+
+ u32 size = type_size_of(value->type);
+ bh_buffer_align(&table_buffer, type_alignment_of(value->type));
+ struct_tag_locations[i] = table_buffer.length;
+
+ bh_buffer_grow(&table_buffer, table_buffer.length + size);
+ constexpr_ctx.data = table_buffer.data;
+ assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
+ table_buffer.length += size;
+
+ i += 1;
+ }
+
+ // Struct methods
+ bh_arr(StructMethodData) method_data=NULL;
+ AstType *ast_type = type->ast_type;
+ if (ast_type && ast_type->kind == Ast_Kind_Struct_Type) {
+ AstStructType *struct_type = (AstStructType *) ast_type;
+ Scope* struct_scope = struct_type->scope;
+
+ if (struct_scope == NULL) goto no_methods;
+
+ fori (i, 0, shlen(struct_scope->symbols)) {
+ AstFunction* node = (AstFunction *) strip_aliases(struct_scope->symbols[i].value);
+ if (node->kind != Ast_Kind_Function) continue;
+ assert(node->entity);
+ assert(node->entity->function == node);
+
+ // Name
+ char *name = struct_scope->symbols[i].key;
+ u32 name_loc = table_buffer.length;
+ u32 name_len = strlen(name);
+ bh_buffer_append(&table_buffer, name, name_len);
+
+ // any data member
+ bh_buffer_align(&table_buffer, 4);
+ u32 data_loc = table_buffer.length;
+ u32 func_idx = get_element_idx(module, node);
+ bh_buffer_write_u32(&table_buffer, func_idx);
+
+ bh_arr_push(method_data, ((StructMethodData) {
+ .name_loc = name_loc,
+ .name_len = name_len,
+ .type = node->type->id,
+ .data_loc = data_loc,
+ }));
+ }
+ }
+
+ no_methods:
+
+ bh_buffer_align(&table_buffer, 4);
+ u32 method_data_base = table_buffer.length;
+
+ i = 0;
+ bh_arr_each(StructMethodData, method, method_data) {
+ WRITE_SLICE(method->name_loc, method->name_len);
+ WRITE_PTR(method->data_loc);
+ bh_buffer_write_u32(&table_buffer, method->type);
+ }
+
+ bh_buffer_align(&table_buffer, 8);
+ u32 struct_tag_base = table_buffer.length;
+
+ fori (i, 0, bh_arr_length(s->meta_tags)) {
+ WRITE_SLICE(struct_tag_locations[i], s->meta_tags[i]->type->id);
+ }
+
+ // Struct name
+ 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));
+
+ if (type->Struct.constructed_from != NULL) {
+ bh_buffer_write_u32(&table_buffer, type->Struct.constructed_from->type_id);
+ } else {
+ bh_buffer_write_u32(&table_buffer, 0);
+ }
+
+ WRITE_SLICE(name_base, name_length);
+ WRITE_SLICE(members_base, s->mem_count);
+ WRITE_SLICE(params_base, bh_arr_length(s->poly_sln));
+ WRITE_SLICE(struct_tag_base, bh_arr_length(s->meta_tags));
+ WRITE_SLICE(method_data_base, bh_arr_length(method_data));
+
+ bh_arr_free(method_data);
+ break;
+ }
+
+ case Type_Kind_PolyStruct: {
+ u32* tag_locations = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(type->PolyStruct.meta_tags));
+ memset(tag_locations, 0, sizeof(u32) * bh_arr_length(type->PolyStruct.meta_tags));
+
+ u32 name_base = table_buffer.length;
+ u32 name_length = strlen(type->PolyStruct.name);
+ bh_buffer_append(&table_buffer, type->PolyStruct.name, name_length);
+
+ u32 tags_count = bh_arr_length(type->PolyStruct.meta_tags);
+ i32 i = 0;
+ bh_arr_each(AstTyped *, tag, type->PolyStruct.meta_tags) {
+ AstTyped* value = *tag;
+
+ // Polymorphic structs are weird in this case, because the tag might not be constructed generically for
+ // the polymorphic structure so it should only be constructed for actual solidified structures.
+ // See core/containers/map.onyx with Custom_Format for an example.
+ if (!(value->flags & Ast_Flag_Comptime)) {
+ tags_count--;
+ continue;
+ }
+
+ assert(value->type);
+
+ u32 size = type_size_of(value->type);
+ bh_buffer_align(&table_buffer, type_alignment_of(value->type));
+ tag_locations[i] = table_buffer.length;
+
+ bh_buffer_grow(&table_buffer, table_buffer.length + size);
+
+ constexpr_ctx.data = table_buffer.data;
+ assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
+ table_buffer.length += size;
+
+ i += 1;
+ }
+
+ bh_buffer_align(&table_buffer, 8);
+ u32 tags_base = table_buffer.length;
+
+ fori (i, 0, tags_count) {
+ WRITE_SLICE(tag_locations[i], type->PolyStruct.meta_tags[i]->type->id);
+ }
+
+ 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, 0);
+ bh_buffer_write_u32(&table_buffer, 0);
+ WRITE_SLICE(name_base, name_length);
+ WRITE_SLICE(tags_base, tags_count);
+
+ break;
+ }
+
+ case Type_Kind_Distinct: {
+ u32 name_base = table_buffer.length;
+ u32 name_length = strlen(type->Distinct.name);
+ bh_buffer_append(&table_buffer, type->Distinct.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->Distinct.base_type->id);
+ WRITE_SLICE(name_base, name_length);
+ break;
+ }
+ }
+ }
+
+ if (context.options->verbose_output == 1) {
+ bh_printf("Type table size: %d bytes.\n", table_buffer.length);
+ }
+
+ WasmDatum type_info_data = {
+ .alignment = 8,
+ .length = table_buffer.length,
+ .data = table_buffer.data,
+ };
+ emit_data_entry(module, &type_info_data);
+ assert(type_info_data.id == type_table_info_data_id);
+
+ bh_arr_each(u32, patch_loc, base_patch_locations) {
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Relative;
+ patch.data_id = type_info_data.id;
+ patch.offset = 0;
+ patch.index = type_info_data.id;
+ patch.location = *patch_loc;
+ bh_arr_push(module->data_patches, patch);
+ }
+
+ WasmDatum type_table_data = {
+ .alignment = POINTER_SIZE,
+ .length = type_count * POINTER_SIZE,
+ .data = table_info,
+ };
+ emit_data_entry(module, &type_table_data);
+
+ fori (i, 0, type_count) {
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Data;
+ patch.data_id = type_info_data.id;
+ patch.offset = table_info[i];
+ patch.index = type_table_data.id;
+ patch.location = i * POINTER_SIZE;
+ bh_arr_push(module->data_patches, patch);
+ }
+
+ Table_Info_Type* tmp_data = bh_alloc(global_heap_allocator, 2 * POINTER_SIZE);
+ tmp_data[0] = 0;
+ tmp_data[1] = type_count;
+ WasmDatum type_table_global_data = {
+ .alignment = POINTER_SIZE,
+ .length = 2 * POINTER_SIZE,
+ .data = tmp_data,
+ };
+ emit_data_entry(module, &type_table_global_data);
+
+ {
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Data;
+ patch.data_id = type_table_data.id;
+ patch.offset = 0;
+ patch.index = type_table_global_data.id;
+ patch.location = 0;
+ bh_arr_push(module->data_patches, patch);
+ }
+
+ return type_table_global_data.id;
+
+#undef WRITE_SLICE
+#undef WRITE_PTR
+#undef PATCH
+}
+
+
+
+static u64 build_foreign_blocks(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, foreign_buffer.length))
+#define WRITE_PTR(val) \
+ bh_buffer_align(&foreign_buffer, POINTER_SIZE); \
+ PATCH; \
+ if (POINTER_SIZE == 4) bh_buffer_write_u32(&foreign_buffer, val); \
+ if (POINTER_SIZE == 8) bh_buffer_write_u64(&foreign_buffer, val);
+#define WRITE_SLICE(ptr, count) \
+ WRITE_PTR(ptr); \
+ if (POINTER_SIZE == 4) bh_buffer_write_u32(&foreign_buffer, count); \
+ if (POINTER_SIZE == 8) bh_buffer_write_u64(&foreign_buffer, count);
+
+ // This is the data behind the "type_table" slice in type_info.onyx
+ #if (POINTER_SIZE == 4)
+ #define Foreign_Block_Type u32
+ #else
+ #define Foreign_Block_Type u64
+ #endif
+ u32 block_count = bh_arr_length(module->foreign_blocks);
+ Foreign_Block_Type* foreign_info = bh_alloc_array(global_heap_allocator, Foreign_Block_Type, block_count); // HACK
+ memset(foreign_info, 0, block_count * sizeof(Foreign_Block_Type));
+
+ bh_buffer foreign_buffer;
+ bh_buffer_init(&foreign_buffer, global_heap_allocator, 4096);
+
+ //
+ // This is necessary because 0 is an invalid offset to store in this
+ // buffer, as 0 will map to NULL. This could be a single byte insertion,
+ // but 64 bytes keeps better alignment.
+ bh_buffer_write_u64(&foreign_buffer, 0);
+
+ u32 index = 0;
+ bh_arr_each(AstForeignBlock *, pfb, module->foreign_blocks) {
+ AstForeignBlock *fb = *pfb;
+
+ u32 funcs_length = 0;
+
+ u32 *name_offsets = bh_alloc_array(global_scratch_allocator, u32, shlen(fb->scope->symbols));
+ u32 *name_lengths = bh_alloc_array(global_scratch_allocator, u32, shlen(fb->scope->symbols));
+ u32 *func_types = bh_alloc_array(global_scratch_allocator, u32, shlen(fb->scope->symbols));
+
+ fori (i, 0, shlen(fb->scope->symbols)) {
+ AstFunction *func = (AstFunction *) fb->scope->symbols[i].value;
+ if (func->kind != Ast_Kind_Function) continue;
+
+ u32 func_name_base = foreign_buffer.length;
+ u32 func_name_length = func->foreign_name->length;
+ bh_buffer_append(&foreign_buffer, func->foreign_name->text, func_name_length);
+
+ name_offsets[funcs_length] = func_name_base;
+ name_lengths[funcs_length] = func_name_length;
+ func_types[funcs_length] = func->type->id;
+ funcs_length++;
+ }
+
+ bh_buffer_align(&foreign_buffer, 8);
+ u32 funcs_base = foreign_buffer.length;
+
+ fori (i, 0, (i64) funcs_length) {
+ bh_buffer_align(&foreign_buffer, POINTER_SIZE);
+ WRITE_SLICE(name_offsets[i], name_lengths[i]);
+ bh_buffer_write_u32(&foreign_buffer, func_types[i]);
+ }
+
+ u32 name_base = foreign_buffer.length;
+ u32 name_length = fb->module_name->length;
+ bh_buffer_append(&foreign_buffer, fb->module_name->text, name_length);
+ bh_buffer_align(&foreign_buffer, 8);
+
+ foreign_info[index] = foreign_buffer.length;
+ WRITE_SLICE(name_base, name_length);
+ WRITE_SLICE(funcs_base, funcs_length);
+ index++;
+ }
+
+
+ if (context.options->verbose_output == 1) {
+ bh_printf("Foreign blocks size: %d bytes.\n", foreign_buffer.length);
+ }
+
+ WasmDatum foreign_info_data = {
+ .alignment = 8,
+ .length = foreign_buffer.length,
+ .data = foreign_buffer.data,
+ };
+ emit_data_entry(module, &foreign_info_data);
+
+ bh_arr_each(u32, patch_loc, base_patch_locations) {
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Relative;
+ patch.data_id = foreign_info_data.id;
+ patch.offset = 0;
+ patch.index = foreign_info_data.id;
+ patch.location = *patch_loc;
+ bh_arr_push(module->data_patches, patch);
+ }
+
+ WasmDatum foreign_table_data = {
+ .alignment = POINTER_SIZE,
+ .length = block_count * POINTER_SIZE,
+ .data = foreign_info,
+ };
+ emit_data_entry(module, &foreign_table_data);
+
+ fori (i, 0, block_count) {
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Data;
+ patch.data_id = foreign_info_data.id;
+ patch.offset = foreign_info[i];
+ patch.index = foreign_table_data.id;
+ patch.location = i * POINTER_SIZE;
+ bh_arr_push(module->data_patches, patch);
+ }
+
+ Foreign_Block_Type* tmp_data = bh_alloc(global_heap_allocator, 2 * POINTER_SIZE);
+ tmp_data[0] = 0;
+ tmp_data[1] = block_count;
+ WasmDatum foreign_table_global_data = {
+ .alignment = POINTER_SIZE,
+ .length = 2 * POINTER_SIZE,
+ .data = tmp_data,
+ };
+ emit_data_entry(module, &foreign_table_global_data);
+
+ {
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Data;
+ patch.data_id = foreign_table_data.id;
+ patch.location = 0;
+ patch.index = foreign_table_global_data.id;
+ patch.offset = 0;
+ bh_arr_push(module->data_patches, patch);
+ }
+
+ return foreign_table_global_data.id;
+
+#undef WRITE_SLICE
+#undef WRITE_PTR
+#undef PATCH
+}
+
+
+
+static u64 build_tagged_procedures(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, tag_proc_buffer.length))
+#define WRITE_PTR(val) \
+ bh_buffer_align(&tag_proc_buffer, POINTER_SIZE); \
+ PATCH; \
+ if (POINTER_SIZE == 4) bh_buffer_write_u32(&tag_proc_buffer, val); \
+ if (POINTER_SIZE == 8) bh_buffer_write_u64(&tag_proc_buffer, val);
+#define WRITE_SLICE(ptr, count) \
+ WRITE_PTR(ptr); \
+ if (POINTER_SIZE == 4) bh_buffer_write_u32(&tag_proc_buffer, count); \
+ if (POINTER_SIZE == 8) bh_buffer_write_u64(&tag_proc_buffer, count);
+
+ #if (POINTER_SIZE == 4)
+ #define Tagged_Procedure_Type u32
+ #else
+ #define Tagged_Procedure_Type u64
+ #endif
+ u32 proc_count = bh_arr_length(module->procedures_with_tags);
+ Tagged_Procedure_Type* tag_proc_info = bh_alloc_array(global_heap_allocator, Tagged_Procedure_Type, proc_count); // HACK
+ memset(tag_proc_info, 0, proc_count * sizeof(Tagged_Procedure_Type));
+
+ bh_buffer tag_proc_buffer;
+ bh_buffer_init(&tag_proc_buffer, global_heap_allocator, 4096);
+
+ u32 proc_info_data_id = NEXT_DATA_ID(module);
+
+ ConstExprContext constexpr_ctx;
+ constexpr_ctx.module = module;
+ constexpr_ctx.data_id = proc_info_data_id;
+
+ //
+ // This is necessary because 0 is an invalid offset to store in this
+ // buffer, as 0 will map to NULL. This could be a single byte insertion,
+ // but 64 bytes keeps better alignment.
+ bh_buffer_write_u64(&tag_proc_buffer, 0);
+
+ u32 index = 0;
+ bh_arr_each(AstFunction *, pfunc, module->procedures_with_tags) {
+ AstFunction *func = *pfunc;
+ if (!should_emit_function(func)) {
+ proc_count--;
+ continue;
+ }
+
+ u32 tag_count = bh_arr_length(func->tags);
+ u32 *tag_data_offsets = bh_alloc_array(global_scratch_allocator, u32, tag_count);
+ u32 *tag_data_types = bh_alloc_array(global_scratch_allocator, u32, tag_count);
+
+ u32 tag_index = 0;
+ bh_arr_each(AstTyped *, ptag, func->tags) {
+ AstTyped *tag = *ptag;
+ bh_buffer_align(&tag_proc_buffer, type_alignment_of(tag->type));
+
+ tag_data_offsets[tag_index ] = tag_proc_buffer.length;
+ tag_data_types [tag_index++] = tag->type->id;
+
+ u32 size = type_size_of(tag->type);
+ bh_buffer_grow(&tag_proc_buffer, tag_proc_buffer.length + size);
+
+ constexpr_ctx.data = tag_proc_buffer.data;
+ emit_constexpr(&constexpr_ctx, tag, tag_proc_buffer.length);
+ tag_proc_buffer.length += size;
+ }
+
+ bh_buffer_align(&tag_proc_buffer, 4);
+ u32 tag_array_base = tag_proc_buffer.length;
+ fori (i, 0, tag_count) {
+ PATCH;
+ bh_buffer_write_u32(&tag_proc_buffer, tag_data_offsets[i]);
+ bh_buffer_write_u32(&tag_proc_buffer, tag_data_types[i]);
+ }
+
+ bh_buffer_align(&tag_proc_buffer, 4);
+ tag_proc_info[index++] = tag_proc_buffer.length;
+
+ bh_buffer_write_u32(&tag_proc_buffer, get_element_idx(module, func));
+ bh_buffer_write_u32(&tag_proc_buffer, func->type->id);
+ WRITE_SLICE(tag_array_base, tag_count);
+ }
+
+ if (context.options->verbose_output == 1) {
+ bh_printf("Tagged procedure size: %d bytes.\n", tag_proc_buffer.length);
+ }
+
+ WasmDatum proc_info_data = {
+ .alignment = 8,
+ .length = tag_proc_buffer.length,
+ .data = tag_proc_buffer.data,
+ };
+ emit_data_entry(module, &proc_info_data);
+ assert(proc_info_data.id == proc_info_data_id);
+
+ bh_arr_each(u32, patch_loc, base_patch_locations) {
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Relative;
+ patch.data_id = proc_info_data.id;
+ patch.location = *patch_loc;
+ patch.index = proc_info_data.id;
+ patch.offset = 0;
+ bh_arr_push(module->data_patches, patch);
+ }
+
+ WasmDatum proc_table_data = {
+ .alignment = POINTER_SIZE,
+ .length = proc_count * POINTER_SIZE,
+ .data = tag_proc_info,
+ };
+ emit_data_entry(module, &proc_table_data);
+
+ fori (i, 0, proc_count) {
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Data;
+ patch.data_id = proc_info_data.id;
+ patch.offset = tag_proc_info[i];
+ patch.index = proc_table_data.id;
+ patch.location = i * POINTER_SIZE;
+ bh_arr_push(module->data_patches, patch);
+ }
+
+ Tagged_Procedure_Type* tmp_data = bh_alloc(global_heap_allocator, 2 * POINTER_SIZE);
+ tmp_data[0] = 0;
+ tmp_data[1] = proc_count;
+ WasmDatum proc_table_global_data = {
+ .alignment = POINTER_SIZE,
+ .length = 2 * POINTER_SIZE,
+ .data = tmp_data,
+ };
+ emit_data_entry(module, &proc_table_global_data);
+
+ {
+ DatumPatchInfo patch;
+ patch.kind = Datum_Patch_Data;
+ patch.offset = 0;
+ patch.data_id = proc_table_data.id;
+ patch.index = proc_table_global_data.id;
+ patch.location = 0;
+ bh_arr_push(module->data_patches, patch);
+ }
+
+ return proc_table_global_data.id;
+
+#undef WRITE_SLICE
+#undef WRITE_PTR
+#undef PATCH
+}
+
+++ /dev/null
-#ifndef ONYXASTNODES_H
-#define ONYXASTNODES_H
-
-#include "stb_ds.h"
-#include "lex.h"
-#include "types.h"
-#include "errors.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(DirectiveInit) \
- NODE(DirectiveLibrary) \
- NODE(DirectiveRemove) \
- \
- 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(DistinctType) \
- \
- NODE(Binding) \
- NODE(Alias) \
- NODE(Injection) \
- NODE(MemRes) \
- NODE(Include) \
- NODE(UsePackage) \
- NODE(Global) \
- NODE(Param) \
- NODE(Function) \
- NODE(OverloadedFunction) \
- NODE(Interface) \
- NODE(Constraint) \
- \
- NODE(PolyParam) \
- NODE(PolySolution) \
- NODE(SolidifiedFunction) \
- NODE(PolyProc) \
- NODE(PolyQuery) \
- \
- NODE(Note) \
- NODE(CallSite) \
- \
- NODE(CodeBlock) \
- NODE(DirectiveInsert) \
- NODE(Macro) \
- \
- NODE(ForeignBlock) \
- \
- NODE(Package) \
- \
- NODE(ZeroValue)
-
-#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;
- char* name;
- Table(AstNode *) symbols;
-} Scope;
-
-
-typedef enum AstKind {
- Ast_Kind_Error,
- Ast_Kind_Package,
- Ast_Kind_Load_File,
- Ast_Kind_Load_Path,
- Ast_Kind_Load_All,
- Ast_Kind_Library_Path,
- Ast_Kind_Memres,
-
- Ast_Kind_Binding,
- Ast_Kind_Alias,
- Ast_Kind_Injection,
- Ast_Kind_Function,
- Ast_Kind_Overloaded_Function,
- Ast_Kind_Polymorphic_Proc,
- Ast_Kind_Polymorph_Query,
- Ast_Kind_Interface,
- Ast_Kind_Constraint,
- Ast_Kind_Constraint_Sentinel,
- 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_Distinct_Type,
- 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_Directive_Init,
- Ast_Kind_Directive_Library,
- Ast_Kind_Directive_Remove,
- Ast_Kind_Call_Site,
-
- Ast_Kind_Code_Block,
- Ast_Kind_Directive_Insert,
- Ast_Kind_Macro,
- Ast_Kind_Do_Block,
-
- Ast_Kind_Foreign_Block,
-
- Ast_Kind_Zero_Value,
-
- 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_Const = BH_BIT(1),
- Ast_Flag_Comptime = BH_BIT(2),
- Ast_Flag_Private_Package = BH_BIT(3),
- Ast_Flag_Private_File = BH_BIT(4),
-
- // Function flags
- Ast_Flag_Function_Used = BH_BIT(5),
-
- // Expression flags
- Ast_Flag_Expr_Ignored = BH_BIT(6),
- Ast_Flag_Address_Taken = BH_BIT(7),
-
- // Type flags
- Ast_Flag_Type_Is_Resolved = BH_BIT(8),
-
- Ast_Flag_No_Clone = BH_BIT(9),
-
- Ast_Flag_Cannot_Take_Addr = BH_BIT(10),
-
- // HACK: NullProcHack
- Ast_Flag_Proc_Is_Null = BH_BIT(11),
-
- Ast_Flag_From_Polymorphism = BH_BIT(12),
-
- Ast_Flag_Incomplete_Body = BH_BIT(13),
-
- Ast_Flag_Array_Literal_Typed = BH_BIT(14),
-
- Ast_Flag_Has_Been_Symres = BH_BIT(15),
-
- Ast_Flag_Has_Been_Checked = BH_BIT(16),
-
- Ast_Flag_Static_If_Resolved = BH_BIT(17),
-
- Ast_Flag_Symbol_Invisible = BH_BIT(18),
-
- Ast_Flag_Header_Check_No_Error = BH_BIT(19),
-
- Ast_Flag_Decl_Followed_By_Init = BH_BIT(20),
-
- Ast_Flag_Param_Symbol_Dirty = BH_BIT(21),
-
- Ast_Flag_Dead = BH_BIT(22),
-} 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_Subscript_Equals = 37,
- Binary_Op_Ptr_Subscript = 38,
-
- 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_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,
-
- ONYX_INTRINSIC_ATOMIC_WAIT,
- ONYX_INTRINSIC_ATOMIC_NOTIFY,
-
- ONYX_INTRINSIC_ATOMIC_FENCE,
-
- ONYX_INTRINSIC_ATOMIC_LOAD,
- ONYX_INTRINSIC_ATOMIC_STORE,
- ONYX_INTRINSIC_ATOMIC_ADD,
- ONYX_INTRINSIC_ATOMIC_SUB,
- ONYX_INTRINSIC_ATOMIC_AND,
- ONYX_INTRINSIC_ATOMIC_OR,
- ONYX_INTRINSIC_ATOMIC_XOR,
- ONYX_INTRINSIC_ATOMIC_XCHG,
- ONYX_INTRINSIC_ATOMIC_CMPXCHG,
-} 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 enum ConstraintCheckStatus {
- Constraint_Check_Status_Queued,
- Constraint_Check_Status_Failed,
- Constraint_Check_Status_Success,
-} ConstraintCheckStatus;
-
-typedef struct ConstraintContext {
- bh_arr(AstConstraint *) constraints;
- ConstraintCheckStatus *constraint_checks;
- b32 constraints_met : 1;
- b32 produce_errors : 1;
-} ConstraintContext;
-
-
-typedef struct Arguments Arguments;
-struct Arguments {
- bh_arr(AstTyped *) values;
- bh_arr(AstNamedValue *) named_values;
-
- // How many arguments were not baked.
- i32 used_argument_count;
-};
-
-
-// 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 AstUnaryOp { AstTyped_base; UnaryOp operation; AstTyped *expr; };
-struct AstStrLit { AstTyped_base; u64 data_id; u64 length; b32 is_cstr: 1; };
-struct AstLocal { AstTyped_base; };
-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 AstNumLit {
- AstTyped_base;
- union {
- i32 i;
- i64 l;
- f32 f;
- f64 d;
- } value;
-
- b32 was_hex_literal : 1;
-};
-struct AstBinaryOp {
- AstTyped_base;
- BinaryOp operation;
- AstTyped *left, *right;
-
- Arguments *overload_args; // This is set of the binary operator is attempted to be overloaded
- // but isnt successful yet.
- AstBinaryOp *potential_substitute;
-};
-struct AstAddressOf {
- AstTyped_base;
- AstTyped *expr;
-
- AstBinaryOp *potential_substitute;
-
- // This is set by check_method_call.
- // If set, the address of node can be removed if the
- // type checking does not pass for it. This makes it
- // possible to have something that will optionally
- // have its address taken, if necessary.
- b32 can_be_removed : 1;
-};
-struct AstArgument {
- AstTyped_base;
-
- AstTyped *value;
-
- VarArgKind va_kind;
- b32 is_baked : 1;
- b32 pass_as_any : 1;
-};
-struct AstSubscript {
- AstTyped_base;
- BinaryOp __unused_operation; // This will be set to Binary_Op_Subscript
- AstTyped *addr;
- AstTyped *expr;
-
- Arguments *overload_args; // This is set of the binary operator is attempted to be overloaded
- // but isnt successful yet.
- AstBinaryOp *potential_substitute;
-
- 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;
-
- AstTyped *filename_expr;
- char *filename; // The parsed file name, with '\' sequences removed and resolved to a particular file if possible.
-
- u32 data_id, 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 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 AstZeroValue {
- AstTyped_base;
-};
-
-struct AstDirectiveSolidify {
- AstTyped_base;
-
- AstFunction* 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;
-
- u32 statement_idx;
-};
-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;
- b32 no_close : 1;
-};
-struct AstIfWhile {
- AstNode_base;
-
- Scope *scope;
- AstNode* initialization;
-
- AstTyped *cond;
-
- AstBlock *true_stmt;
- AstBlock *false_stmt;
-
- union {
- // Used by Static_If
- struct {
- Scope *defined_in_scope;
- bh_arr(struct Entity *) true_entities;
- bh_arr(struct Entity *) false_entities;
- };
-
- // Used by While
- b32 bottom_test;
- };
-};
-typedef struct AstIfWhile AstIf;
-typedef struct AstIfWhile AstWhile;
-
-typedef enum SwitchKind {
- Switch_Kind_Integer,
- Switch_Kind_Use_Equals,
-} SwitchKind;
-
-typedef struct CaseToBlock {
- AstTyped *original_value;
- AstBinaryOp *comparison;
- AstBlock *block;
-} CaseToBlock;
-
-struct AstSwitchCase {
- AstNode_base;
-
- // NOTE: All expressions that end up in this block
- bh_arr(AstTyped *) values;
-
- AstBlock *block;
-
- b32 is_default: 1; // Could this be inferred by the values array being null?
-};
-
-struct AstSwitch {
- AstNode_base;
-
- Scope *scope;
- AstNode* initialization;
-
- AstTyped *expr;
-
- AstBlock *case_block;
-
- bh_arr(AstSwitchCase *) cases;
- AstBlock *default_case;
-
- i32 yield_return_index;
- SwitchKind switch_kind;
-
- union {
- struct {
- // 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;
- };
-
- struct {
- // NOTE: This is a mapping from the '==' binary op node to
- // a pointer to the block that it is associated with.
- bh_arr(CaseToBlock) case_exprs;
- };
- };
-};
-
-// 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; \
- void* __unused; \
- u64 type_id; \
- Type* type
-struct AstType { AstType_base; };
-
-struct AstBasicType { AstType_base; Type* basic_type; };
-struct AstPointerType { AstType_base; AstType* elem; };
-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 AstFunctionType {
- AstType_base;
-
- // Used in a rare case in solve_for_polymorphic_param_type.
- Type *partial_function_type;
-
- AstType* return_type;
-
- u64 param_count;
- AstType* params[];
-};
-struct AstStructType {
- AstType_base;
- char *name;
-
- bh_arr(AstStructMember *) members;
- bh_arr(AstTyped *) meta_tags;
-
- // u32 min_alignment, min_size;
- AstTyped *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: This type is used when the structure has not been
- // completely generated, but is a valid pointer to where the
- // type will be generated to.
- Type *pending_type;
-
- // NOTE: Used to store statically bound expressions in the struct.
- Scope* scope;
-
- struct Entity* entity_type;
- struct Entity* entity_defaults;
-
- OnyxFilePos polymorphic_error_loc;
- ConstraintContext constraints;
-
- bh_arr(AstType *) polymorphic_argument_types;
- bh_arr(AstPolySolution) polymorphic_arguments;
-
- b32 pending_type_is_valid : 1;
- b32 is_union : 1;
- b32 is_packed : 1;
- b32 ready_to_build_type : 1;
-};
-struct AstStructMember {
- AstTyped_base;
- AstTyped* initial_value;
-
- bh_arr(AstTyped *) meta_tags;
-
- b32 is_used : 1;
-};
-struct AstPolyStructParam {
- AstTyped_base;
-};
-struct AstPolyStructType {
- AstType_base;
- char *name;
-
- Scope *scope;
- bh_arr(AstPolyStructParam) poly_params;
- 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;
-
- b32 is_flags : 1;
-};
-struct AstEnumValue { AstTyped_base; AstTyped* 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;
-};
-struct AstDistinctType {
- AstType_base;
- char *name;
- AstType *base_type;
- Type *dtcache;
-};
-
-// Top level nodes
-struct AstBinding { AstTyped_base; AstNode* node; };
-struct AstAlias { AstTyped_base; AstTyped* alias; };
-struct AstInclude { AstNode_base; AstTyped* name_node; char* name; };
-struct AstInjection {
- AstTyped_base;
- AstTyped* full_loc;
- AstTyped* to_inject;
- AstTyped* dest;
- OnyxToken *symbol;
-};
-struct AstMemRes {
- AstTyped_base;
- AstTyped *initial_value;
-
- struct Entity *type_entity;
-
- b32 threadlocal : 1;
-
- // Set and used in the wasm emission.
- u32 data_id;
- u32 tls_offset;
-};
-struct AstGlobal {
- AstTyped_base;
-
- char* 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 is_used : 1;
- b32 use_processed : 1;
- b32 is_baked : 1;
-};
-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;
-
- b32 locked : 1;
- b32 only_local_functions : 1;
-};
-
-// @CLEANUP: Is this really necessary?
-typedef struct InterfaceParam {
- OnyxToken *value_token;
- OnyxToken *type_token;
-} InterfaceParam;
-
-typedef struct InterfaceConstraint {
- AstTyped *expr;
- AstType *expected_type_expr;
- Type *expected_type;
-
- b32 invert_condition: 1;
-} InterfaceConstraint;
-
-struct AstInterface {
- AstTyped_base;
- char *name;
-
- bh_arr(InterfaceParam) params;
- bh_arr(InterfaceConstraint) exprs;
-};
-
-typedef enum ConstraintPhase {
- Constraint_Phase_Waiting_To_Be_Queued = 0,
- Constraint_Phase_Cloning_Expressions = 1,
- Constraint_Phase_Checking_Expressions = 2,
- Constraint_Phase_Finished = 3,
-} ConstraintPhase;
-
-struct AstConstraint {
- AstNode_base;
-
- ConstraintPhase phase;
-
- AstInterface *interface;
- bh_arr(AstType *) type_args;
-
- ConstraintCheckStatus *report_status;
-
- Scope* scope;
- bh_arr(InterfaceConstraint) exprs;
- u32 expr_idx;
-};
-
-
-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;
-
-typedef enum PolyProcLookupMethod {
- PPLM_By_Arguments,
- PPLM_By_Function_Type,
-} PolyProcLookupMethod;
-
-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;
- struct Entity *func_header_entity;
-};
-
-struct AstFunction {
- AstTyped_base;
-
- Scope *scope;
-
- bh_arr(AstParam) params;
- AstType* return_type;
-
- AstBlock *body;
-
- char* 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;
- Scope* poly_scope;
-
- // 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;
-
- OnyxToken* closing_brace;
-
- 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;
-
- ConstraintContext constraints;
-
- bh_arr(AstTyped *) tags;
-
- // Polymorphic procedures use the following fields
- Scope *parent_scope_of_poly_proc;
- bh_arr(AstPolyParam) poly_params;
-
- bh_arr(AstPolySolution) known_slns;
-
- Table(AstSolidifiedFunction) concrete_funcs;
- bh_imap active_queries;
-
- bh_arr(AstNode *) nodes_that_need_entities_after_clone;
-
- b32 is_exported : 1;
- b32 is_foreign : 1;
- b32 is_intrinsic : 1;
-};
-
-struct AstPolyQuery {
- AstNode_base;
-
- AstFunction *proc;
- PolyProcLookupMethod pp_lookup;
- ptr given;
- OnyxToken *error_loc;
-
- bh_arr(AstPolySolution) slns;
-
- AstFunction *function_header;
-
- b32 error_on_fail : 1; // Whether or not to report errors on failing to match.
- b32 successful_symres : 1; // If something successful happened in symbol resolution
-};
-
-
-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_name_expr;
- AstTyped* export;
-};
-
-struct AstDirectiveDefined {
- AstTyped_base;
- AstTyped *expr;
-
- b32 is_defined: 1;
-};
-
-struct AstDirectiveRemove {
- AstNode_base;
-};
-
-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 {
- AstTyped_base;
-
- AstTyped *code_expr;
-};
-
-struct AstDirectiveInit {
- AstNode_base;
-
- AstTyped *init_proc;
- bh_arr(AstDirectiveInit *) dependencies;
-};
-
-struct AstMacro {
- AstTyped_base;
-
- AstTyped* body;
-};
-
-struct AstDirectiveLibrary {
- AstNode_base;
-
- AstTyped *library_symbol; // This should resolve to a string literal
- char *library_name;
-};
-
-struct AstForeignBlock {
- AstTyped_base;
-
- Scope* scope;
- OnyxToken *module_name;
- bh_arr(struct Entity *) captured_entities;
-
- u32 foreign_block_number;
-};
-
-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_Failed,
-
- 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_Enum_Value,
- Entity_Type_Type_Alias,
- Entity_Type_Memory_Reservation_Type,
- Entity_Type_Use,
- Entity_Type_Interface,
- Entity_Type_Constraint_Check,
- Entity_Type_Polymorphic_Proc,
- Entity_Type_Polymorph_Query,
- Entity_Type_Macro,
- Entity_Type_Foreign_Block,
- Entity_Type_Foreign_Function_Header,
- Entity_Type_Temp_Function_Header, // Same as a Function_Header, except it disappears after it checks completely.
- 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;
- AstEnumValue *enum_value;
- AstMemRes *mem_res;
- AstFunction *poly_proc;
- AstPolyQuery *poly_query;
- AstForeignBlock *foreign_block;
- AstMacro *macro;
- AstUse *use;
- AstInterface *interface;
- AstConstraint *constraint;
- AstDirectiveLibrary *library;
- };
-} 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);
-void entity_change_type(EntityHeap* entities, Entity *ent, EntityType new_type);
-
-// 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);
-
-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_RUN,
- 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_Onyx = 1,
- Runtime_Wasi = 2,
- Runtime_Js = 3,
- Runtime_Custom = 4,
-};
-
-
-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;
- b32 use_multi_threading : 1;
- b32 generate_foreign_info : 1;
-
- Runtime runtime;
-
- bh_arr(const char *) included_folders;
- bh_arr(const char *) files;
- const char* target_file;
- const char* documentation_file;
-
- b32 debug_enabled;
-
- i32 passthrough_argument_count;
- char** passthrough_argument_data;
-};
-
-typedef struct Context Context;
-struct Context {
- 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_almost_detected : 1;
- 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 AstGlobal builtin_heap_start;
-extern AstGlobal builtin_stack_top;
-extern AstGlobal builtin_tls_base;
-extern AstGlobal builtin_tls_size;
-extern AstType *builtin_string_type;
-extern AstType *builtin_cstring_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 AstType *builtin_link_options_type;
-extern AstTyped *type_table_node;
-extern AstTyped *foreign_blocks_node;
-extern AstType *foreign_block_type;
-extern AstTyped *tagged_procedures_node;
-extern AstFunction *builtin_initialize_data_segments;
-extern AstFunction *builtin_run_init_procedures;
-extern bh_arr(AstFunction *) init_procedures;
-
-typedef struct BuiltinSymbol {
- char* package;
- char* sym;
- AstNode* node;
-} BuiltinSymbol;
-
-extern const BuiltinSymbol builtin_symbols[];
-
-typedef struct IntrinsicMap {
- char* name;
- OnyxIntrinsic intrinsic;
-} IntrinsicMap;
-
-typedef Table(OnyxIntrinsic) IntrinsicTable;
-extern IntrinsicTable intrinsic_table;
-
-extern bh_arr(OverloadOption) operator_overloads[Binary_Op_Count];
-
-void initialize_builtins(bh_allocator a);
-void initalize_special_globals();
-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);
-
-typedef enum TypeMatch {
- TYPE_MATCH_SUCCESS,
- TYPE_MATCH_FAILED,
- TYPE_MATCH_YIELD,
- TYPE_MATCH_SPECIAL, // Only used for nest polymorph function lookups
-} TypeMatch;
-#define unify_node_and_type(node, type) (unify_node_and_type_((node), (type), 1))
-TypeMatch unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent);
-Type* resolve_expression_type(AstTyped* node);
-
-i64 get_expression_integer_value(AstTyped* node, b32 *out_is_valid);
-char *get_expression_string_value(AstTyped* node, b32 *out_is_valid);
-
-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);
-AstUnaryOp* make_cast(bh_allocator a, AstTyped* expr, Type* to);
-AstZeroValue* make_zero_value(bh_allocator a, OnyxToken *token, Type* type);
-
-void arguments_initialize(Arguments* args);
-b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg, b32 insert_zero_values);
-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);
-void arguments_clear_baked_flags(Arguments* args);
-TypeMatch check_arguments_against_type(Arguments* args, TypeFunction* func_type, VarArgKind* va_kind,
- OnyxToken* location, char* func_name, struct OnyxError* error);
-i32 get_argument_buffer_size(TypeFunction* type, 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);
-
-void insert_poly_sln_into_scope(Scope* scope, AstPolySolution *sln);
-TypeMatch find_polymorphic_sln(AstPolySolution *out, AstPolyParam *param, AstFunction *func, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg);
-AstFunction* polymorphic_proc_lookup(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn);
-AstFunction* polymorphic_proc_solidify(AstFunction* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
-AstNode* polymorphic_proc_try_solidify(AstFunction* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
-AstFunction* polymorphic_proc_build_only_header(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual);
-AstFunction* polymorphic_proc_build_only_header_with_slns(AstFunction* pp, bh_arr(AstPolySolution) slns, b32 error_if_failed);
-b32 potentially_convert_function_to_polyproc(AstFunction *func);
-AstPolyCallType* convert_call_to_polycall(AstCall* call);
-
-void add_overload_option(bh_arr(OverloadOption)* poverloads, u64 precedence, AstTyped* overload);
-AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads, Arguments* args);
-AstTyped* find_matching_overload_by_type(bh_arr(OverloadOption) overloads, Type* type);
-void report_unable_to_match_overload(AstCall* call, bh_arr(OverloadOption) overloads);
-
-void expand_macro(AstCall** pcall, AstFunction* template);
-AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite, b32 error_if_failed);
-
-Type* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos, b32 error_if_failed);
-
-// NOTE: Useful inlined functions
-static inline b32 is_lval(AstNode* node) {
- node = strip_aliases(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)
- || (node->kind == Ast_Kind_Constraint_Sentinel)) // Bit of a hack, but this makes constraints like 'T->foo()' work.
- 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 b32 node_is_addressable_literal(AstNode* node) {
- return (node->kind == Ast_Kind_Struct_Literal)
- || (node->kind == Ast_Kind_Array_Literal);
-}
-
-static inline Type* get_expression_type(AstTyped* expr) {
- switch (expr->kind) {
- case Ast_Kind_Block: case Ast_Kind_If: case Ast_Kind_While: return NULL;
- default: return expr->type;
- }
-}
-
-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 (AstFunction *) node;
- if (node->kind == Ast_Kind_Macro) return get_function_from_node((AstNode*) ((AstMacro *) node)->body);
- return NULL;
-}
-
-static inline void convert_polyproc_to_function(AstFunction *func) {
- if (func->kind != Ast_Kind_Polymorphic_Proc) return;
-
- func->kind = Ast_Kind_Function;
- func->parent_scope_of_poly_proc = NULL;
- func->poly_params = NULL;
- func->known_slns = NULL;
- func->concrete_funcs = NULL;
- func->active_queries.hashes = NULL;
- func->active_queries.entries = NULL;
- func->poly_scope = NULL;
- func->entity = NULL;
- func->type = NULL;
- func->tags = NULL;
-}
-
-static inline void convert_function_to_polyproc(AstFunction *func) {
- if (func->kind != Ast_Kind_Function) return;
-
- func->kind = Ast_Kind_Polymorphic_Proc;
- func->parent_scope_of_poly_proc = func->scope->parent;
- func->scope = NULL;
- if (func->entity) entity_change_type(&context.entities, func->entity, Entity_Type_Polymorphic_Proc);
-}
-
-#endif // #ifndef ONYXASTNODES_H
+++ /dev/null
-#ifndef BH_H
-#define BH_H
-
-// NOTE: For lseek64
-#define _LARGEFILE64_SOURCE
-
-#if defined(_WIN32) || defined(_WIN64)
- #define _BH_WINDOWS 1
-#endif
-
-#if defined(__unix__)
- #define _BH_LINUX 1
-#endif
-
-#include <sys/stat.h>
-#include <malloc.h>
-#include <time.h>
-
-#ifdef _BH_LINUX
- #include <errno.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <dirent.h>
-#endif
-
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h> // TODO: Replace with needed functions
-#include <stdint.h>
-#include <assert.h>
-#include <stdio.h>
-
-#if defined(_MSC_VER) && !defined(_WINDOWS_)
- #include "small_windows.h"
-#endif
-
-//-------------------------------------------------------------------------------------
-// Better types
-//-------------------------------------------------------------------------------------
-typedef uint8_t u8;
-typedef uint16_t u16;
-typedef uint32_t u32;
-typedef uint64_t u64;
-typedef int8_t i8;
-typedef int16_t i16;
-typedef int32_t i32;
-typedef int64_t i64;
-typedef int64_t isize;
-typedef i32 b32;
-typedef void* ptr;
-typedef float f32;
-typedef double f64;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Better character functions
-//-------------------------------------------------------------------------------------
-b32 char_is_alpha(const char a);
-b32 char_is_num(const char a);
-b32 char_is_alphanum(const char a);
-char charset_contains(const char* charset, char ch);
-b32 char_is_whitespace(const char a);
-b32 char_in_range(const char lo, const char hi, const char a);
-i64 chars_match(char* ptr1, char* ptr2);
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Better math functions
-//-------------------------------------------------------------------------------------
-#define bh_max(a, b) ((a) > (b) ? (a) : (b))
-#define bh_min(a, b) ((a) < (b) ? (a) : (b))
-#define bh_clamp(v, a, b) (bh_min((b), bh_max((a), (v))))
-#define bh_abs(x) ((x) < 0 ? -(x) : (x))
-#define size_of(x) (isize) sizeof(x)
-
-static inline u64 log2_dumb(u64 n) {
- switch (n) {
- case 1 << 0: return 0;
- case 1 << 1: return 1;
- case 1 << 2: return 2;
- case 1 << 3: return 3;
- case 1 << 4: return 4;
- case 1 << 5: return 5;
- case 1 << 6: return 6;
- case 1 << 7: return 7;
- case 1 << 8: return 8;
- case 1 << 9: return 9;
- case 1 << 10: return 10;
- case 1 << 11: return 11;
- case 1 << 12: return 12;
- case 1 << 13: return 13;
- case 1 << 14: return 14;
- case 1 << 15: return 15;
- case 1 << 16: return 16;
- case 1 << 17: return 17;
- case 1 << 18: return 18;
- case 1 << 19: return 19;
- case 1 << 20: return 20;
- case 1 << 21: return 21;
- case 1 << 22: return 22;
- case 1 << 23: return 23;
- case 1 << 24: return 24;
- case 1 << 25: return 25;
- case 1 << 26: return 26;
- case 1 << 27: return 27;
- case 1 << 28: return 28;
- case 1 << 29: return 29;
- case 1 << 30: return 30;
- case 1 << 31: return 31;
-
- default: return 0;
- }
-}
-
-
-
-static inline const char* bh_num_suffix(u64 i) {
- if (i == 11 || i == 12 || i == 13) return "th";
-
- switch (i % 10) {
- case 0: return "th";
- case 1: return "st";
- case 2: return "nd";
- case 3: return "rd";
- case 4: return "th";
- case 5: return "th";
- case 6: return "th";
- case 7: return "th";
- case 8: return "th";
- case 9: return "th";
-
- default: return "";
- }
-}
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Conversion functions
-//-------------------------------------------------------------------------------------
-
-// Converts an unsigned integer to the unsigned LEB128 format
-u8* uint_to_uleb128(u64 n, i32* output_length);
-u8* int_to_leb128(i64 n, i32* output_length);
-u8* float_to_ieee754(f32 f, b32 reverse);
-u8* double_to_ieee754(f64 f, b32 reverse);
-
-u64 uleb128_to_uint(u8* bytes, i32 *byte_walker);
-
-
-
-//-------------------------------------------------------------------------------------
-// Helpful macros
-//-------------------------------------------------------------------------------------
-#define bh_offset_of(Type, elem) ((isize)&(((Type)*) 0)->elem)
-#define bh_align_of(Type) bh_offset_of(struct { char c; Type member; }, member)
-#define bh_swap(Type, a, b) do { Type tmp = (a); (a) = (b); (b) = tmp; } while(0)
-
-#define bh_align(x, a) if ((x) % (a) != 0) (x) += (a) - ((x) % (a));
-
-#define bh_pointer_add(ptr, amm) ((void *)((u8 *) (ptr) + (amm)))
-#define BH_BIT(x) (1 << (x))
-#define BH_MASK_SET(var, set, mask) ((set) ? ((var) |= (mask)) : ((var) &= ~(mask)))
-
-#define fori(var, lo, hi) for (i64 var = (lo); var < (hi); var++)
-#define forir(var, hi, lo) for (i64 var = (hi); var >= (lo); var--)
-#define forll(T, var, start, step) for (T* var = (start); var != NULL; var = (T *) var->step)
-
-#if defined(BH_DEBUG) && !defined(_BH_WINDOWS)
- #define DEBUG_HERE __asm("int $3")
-#else
- #define DEBUG_HERE
-#endif
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Custom allocators
-//-------------------------------------------------------------------------------------
-
-typedef enum bh_allocator_actions {
- bh_allocator_action_alloc,
- bh_allocator_action_free,
- bh_allocator_action_resize,
-} bh_allocator_actions;
-
-#define BH_ALLOCATOR_PROC(name) \
-ptr name(ptr data, bh_allocator_actions action, \
- isize size, isize alignment, \
- void* prev_memory, \
- u64 flags)
-
-typedef BH_ALLOCATOR_PROC(bh__allocator_proc); // NOTE: so bh__allocator_proc can be used instead of that type
-
-typedef struct bh_allocator {
- bh__allocator_proc* proc; // Procedure that can handle bh_allocator_actions
- ptr data; // Pointer to the other data for the allocator
-} bh_allocator;
-
-typedef enum bh_allocator_flags {
- bh_allocator_flag_clear = 1 // Sets all memory to be 0
-} bh_allocator_flags;
-
-ptr bh_alloc(bh_allocator a, isize size);
-ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment);
-ptr bh_resize(bh_allocator a, ptr data, isize new_size);
-ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment);
-void bh_free(bh_allocator a, ptr data);
-
-#define bh_alloc_item(allocator_, T) (T *) bh_alloc(allocator_, sizeof(T))
-#define bh_alloc_array(allocator_, T, n) (T *) bh_alloc(allocator_, sizeof(T) * (n))
-
-// NOTE: This should get optimized out since alignment should be a power of two
-#define bh__align(x, alignment) ((((x) / alignment) + 1) * alignment)
-
-
-
-
-// HEAP ALLOCATOR
-// Essentially a wrapper for malloc, free and realloc
-bh_allocator bh_heap_allocator(void);
-BH_ALLOCATOR_PROC(bh_heap_allocator_proc);
-
-
-
-
-
-// ARENA ALLOCATOR
-typedef struct bh_arena {
- bh_allocator backing;
- ptr first_arena, current_arena;
- isize size, arena_size; // in bytes
-} bh_arena;
-
-typedef struct bh__arena_internal {
- ptr next_arena;
- void* data; // Not actually a pointer, just used for the offset
-} bh__arena_internal;
-
-void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size);
-void bh_arena_free(bh_arena* alloc);
-bh_allocator bh_arena_allocator(bh_arena* alloc);
-BH_ALLOCATOR_PROC(bh_arena_allocator_proc);
-
-
-
-
-
-
-// SCRATCH ALLOCATOR
-typedef struct bh_scratch {
- bh_allocator backing;
- ptr memory, end, curr;
-} bh_scratch;
-
-void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size);
-void bh_scratch_free(bh_scratch* scratch);
-bh_allocator bh_scratch_allocator(bh_scratch* scratch);
-BH_ALLOCATOR_PROC(bh_scratch_allocator_proc);
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Allocator based string functions
-//-------------------------------------------------------------------------------------
-
-b32 bh_str_starts_with(char* str, char* start);
-b32 bh_str_ends_with(char* str, char* end);
-char* bh_strdup(bh_allocator a, char* str);
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Better files
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_FILE
-
-typedef enum bh_file_error {
- BH_FILE_ERROR_NONE,
- BH_FILE_ERROR_INVALID,
- BH_FILE_ERROR_BAD_FD,
-} bh_file_error;
-
-typedef enum bh_file_mode {
- BH_FILE_MODE_READ = 1 << 0,
- BH_FILE_MODE_WRITE = 1 << 1,
- BH_FILE_MODE_APPEND = 1 << 2,
- BH_FILE_MODE_RW = 1 << 3,
-
- BH_FILE_MODE_MODES = BH_FILE_MODE_READ | BH_FILE_MODE_WRITE | BH_FILE_MODE_APPEND | BH_FILE_MODE_RW
-} bh_file_mode;
-
-typedef enum bh_file_whence {
- BH_FILE_WHENCE_BEGIN = SEEK_SET,
- BH_FILE_WHENCE_CURRENT = SEEK_CUR,
- BH_FILE_WHENCE_END = SEEK_END,
-} bh_file_whence;
-
-#ifdef _BH_WINDOWS
- typedef HANDLE bh_file_descriptor;
-#else
- typedef int bh_file_descriptor;
-#endif
-
-typedef struct bh_file {
- bh_file_descriptor fd;
- char const* filename;
-} bh_file;
-
-typedef enum bh_file_standard {
- BH_FILE_STANDARD_INPUT,
- BH_FILE_STANDARD_OUTPUT,
- BH_FILE_STANDARD_ERROR
-} bh_file_standard;
-
-typedef struct bh_file_contents {
- bh_allocator allocator;
- const char *filename;
- isize length;
- void* data;
- isize line_count;
-} bh_file_contents;
-
-
-#define BH_FILE_TYPE_DIRECTORY 3
-#define BH_FILE_TYPE_FILE 4
-#define BH_FILE_TYPE_LINK 5
-typedef struct bh_file_stats {
- isize size;
- u32 file_type;
-} bh_file_stats;
-
-bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand);
-
-bh_file_error bh_file_create(bh_file* file, char const* filename);
-bh_file_error bh_file_open(bh_file* file, char const* filename);
-bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename);
-bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename);
-b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read);
-b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote);
-i64 bh_file_seek(bh_file* file, i64 offset, bh_file_whence whence);
-i64 bh_file_seek_to(bh_file* file, i64 offset);
-i64 bh_file_seek_to_end(bh_file* file);
-i64 bh_file_skip(bh_file* file, i64 bytes);
-i64 bh_file_tell(bh_file* file);
-bh_file_error bh_file_close(bh_file* file);
-i32 bh_file_read(bh_file* file, void* buffer, isize buff_size);
-i32 bh_file_write(bh_file* file, void* buffer, isize buff_size);
-void bh_file_flush(bh_file* file);
-i64 bh_file_size(bh_file* file);
-b32 bh_file_stat(char const* filename, bh_file_stats* stat);
-b32 bh_file_exists(char const* filename);
-b32 bh_file_remove(char const* filename);
-char* bh_path_get_full_name(char const* filename, bh_allocator a);
-char* bh_path_get_parent(char const* filename, bh_allocator a);
-char* bh_path_convert_separators(char* path);
-
-// This function returns a volatile pointer. Do not store it without copying!
-// `included_folders` is bh_arr(const char *).
-char* bh_lookup_file(char* filename, char* relative_to, char *suffix, b32 add_suffix, const char ** included_folders, b32 search_included_folders);
-
-#define bh_file_read_contents(allocator_, x) _Generic((x), \
- bh_file*: bh_file_read_contents_bh_file, \
- const char*: bh_file_read_contents_direct, \
- char*: bh_file_read_contents_direct)((allocator_), x)
-
-bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file);
-bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename);
-i32 bh_file_contents_free(bh_file_contents* contents);
-
-
-#ifdef _BH_WINDOWS
- typedef struct Windows_Directory_Opened {
- HANDLE hndl;
- WIN32_FIND_DATAA found_file;
- } Windows_Directory_Opened;
-
- typedef Windows_Directory_Opened *bh_dir;
-#else
- typedef DIR *bh_dir;
-#endif
-
-typedef enum bh_dirent_type {
- BH_DIRENT_UNKNOWN,
- BH_DIRENT_BLOCK,
- BH_DIRENT_CHAR,
- BH_DIRENT_DIRECTORY,
- BH_DIRENT_FILE,
- BH_DIRENT_SYMLINK,
- BH_DIRENT_OTHER,
-} bh_dirent_type;
-
-typedef struct bh_dirent {
- bh_dirent_type type;
- u32 id;
- u32 name_length;
- char name[256];
-} bh_dirent;
-
-bh_dir bh_dir_open(char* path);
-b32 bh_dir_read(bh_dir dir, bh_dirent* out);
-void bh_dir_close(bh_dir dir);
-
-#endif
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Alternate printing
-//-------------------------------------------------------------------------------------
-// Barebones implementation of printf. Does not support all format options
-// Currently supports:
-// %c - chars
-// %_(u)d - ints where _ is:
-// nothing - decimal
-// o - octal
-// x - hexadecimal
-// %_(u)l - longs where _ is:
-// nothing - decimal
-// o - octal
-// x - hexadecimal
-// %f - floating points
-// %s - null terminated strings
-// %p - pointers
-// %% - literal %
-
-typedef struct bh__print_format {
- u32 base;
-} bh__print_format;
-
-isize bh_printf(char const *fmt, ...);
-isize bh_printf_va(char const *fmt, va_list va);
-isize bh_printf_err(char const *fmt, ...);
-isize bh_printf_err_va(char const *fmt, va_list va);
-isize bh_fprintf(bh_file* f, char const *fmt, ...);
-isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va);
-char* bh_bprintf(char const *fmt, ...);
-char* bh_bprintf_va(char const *fmt, va_list va);
-char* bh_aprintf(bh_allocator alloc, const char* fmt, ...);
-char* bh_aprintf_va(bh_allocator alloc, const char* fmt, va_list va);
-isize bh_snprintf(char *str, isize n, char const *fmt, ...);
-isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Flexible buffer
-//-------------------------------------------------------------------------------------
-
-typedef struct bh_buffer {
- bh_allocator allocator;
- i32 length, capacity;
- u8* data;
-} bh_buffer;
-
-#ifndef BH_BUFFER_GROW_FORMULA
-#define BH_BUFFER_GROW_FORMULA(x) ((x) > 0 ? ((x) << 1) : 16)
-#endif
-
-void bh_buffer_init(bh_buffer* buffer, bh_allocator alloc, i32 length);
-void bh_buffer_free(bh_buffer* buffer);
-void bh_buffer_clear(bh_buffer* buffer);
-void bh_buffer_grow(bh_buffer* buffer, i32 length);
-void bh_buffer_append(bh_buffer* buffer, const void * data, i32 length);
-void bh_buffer_concat(bh_buffer* buffer, bh_buffer other);
-void bh_buffer_write_byte(bh_buffer* buffer, u8 byte);
-void bh_buffer_write_u32(bh_buffer* buffer, u32 i);
-void bh_buffer_write_u64(bh_buffer* buffer, u64 i);
-void bh_buffer_align(bh_buffer* buffer, u32 alignment);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// Better dynamically-sized arrays
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_ARRAY
-
-typedef struct bh__arr {
- bh_allocator allocator;
- i32 length, capacity;
-} bh__arr;
-
-#ifndef BH_ARR_GROW_FORMULA
-#define BH_ARR_GROW_FORMULA(x) ((x) > 0 ? ((x) << 1) : 4)
-#endif
-
-#define bh_arr(T) T*
-#define bh__arrhead(arr) (((bh__arr *)(arr)) - 1)
-
-#define bh_arr_allocator(arr) (arr ? bh__arrhead(arr)->allocator : bh_heap_allocator())
-#define bh_arr_length(arr) (arr ? bh__arrhead(arr)->length : 0)
-#define bh_arr_capacity(arr) (arr ? bh__arrhead(arr)->capacity : 0)
-#define bh_arr_size(arr) (arr ? bh__arrhead(arr)->capacity * sizeof(*(arr)) : 0)
-#define bh_arr_valid(arr, i) (arr ? (i32)(i) < bh__arrhead(arr)->length : 0)
-
-#define bh_arr_pop(arr) ((arr)[--bh__arrhead(arr)->length])
-#define bh_arr_last(arr) ((arr)[bh__arrhead(arr)->length - 1])
-#define bh_arr_end(arr, i) ((i) >= &(arr)[bh_arr_length(arr)])
-#define bh_arr_start(arr, i) ((i) < (arr))
-
-#define bh_arr_new(allocator_, arr, cap) (bh__arr_grow((allocator_), (void**) &(arr), sizeof(*(arr)), cap))
-#define bh_arr_free(arr) (bh__arr_free((void**) &(arr)))
-#define bh_arr_copy(allocator_, arr) (bh__arr_copy((allocator_), (arr), sizeof(*(arr))))
-
-#define bh_arr_grow(arr, cap) (bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), cap))
-#define bh_arr_shrink(arr, cap) (bh__arr_shrink((void **) &(arr), sizeof(*(arr)), cap))
-#define bh_arr_set_length(arr, n) (bh__arrhead(arr)->length = n)
-
-#define bh_arr_insertn(arr, i, n) (bh__arr_insertn((void **) &(arr), sizeof(*(arr)), i, n))
-
-#define bh_arr_insert_end(arr, n) ( \
- bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + n), \
- bh__arrhead(arr)->length += n)
-
-#define bh_arr_push(arr, value) ( \
- bh_arr_length(arr) + 1 > bh_arr_capacity(arr) ? bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + 1) : 0, \
- arr[bh__arrhead(arr)->length++] = value)
-
-#define bh_arr_set_at(arr, n, value) ( \
- bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), (n) + 1), \
- bh_arr_set_length((arr), bh_max(bh_arr_length(arr), (i32) (n) + 1)), \
- arr[n] = value)
-
-#define bh_arr_is_empty(arr) (arr ? bh__arrhead(arr)->length == 0 : 1)
-#define bh_arr_clear(arr) (arr ? (bh__arrhead(arr)->length = 0) : 0)
-
-#define bh_arr_deleten(arr, i, n) (bh__arr_deleten((void **) &(arr), sizeof(*(arr)), i, n))
-#define bh_arr_fastdelete(arr, i) (arr[i] = arr[--bh__arrhead(arr)->length])
-
-#define bh_arr_each(T, var, arr) for (T* var = (arr); !bh_arr_end((arr), var); var++)
-#define bh_arr_rev_each(T, var, arr) for (T* var = &bh_arr_last((arr)); !bh_arr_start((arr), var); var--)
-
-#define bh_arr_zero(arr) memset(arr, 0, bh_arr_length(arr) * sizeof(*(arr)));
-
-b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap);
-b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap);
-b32 bh__arr_free(void **arr);
-void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize);
-void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems);
-void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems);
-
-#endif
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// STRING HASH TABLE FUNCTIONS
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_TABLE
-
-#ifdef BH_DEFINE
-u64 bh__table_hash_function(const char* str, i32 len, i32 mod) {
- u64 hash = 5381;
- i32 c, l = 0;
- if (len == 0) len = ((u32) 1 << 31) - 1;
-
- while ((c = *str++) && l++ < len) {
- hash = (hash << 5) + hash + c;
- }
-
- return hash % mod;
-}
-#endif
-
-typedef struct bh_table_iterator {
- ptr *tab, *endtab;
- i32 elemsize, arrlen;
- ptr entry;
-} bh_table_iterator;
-
-typedef struct bh__table {
- bh_allocator allocator;
- u64 table_size; // NOTE: u64 since padding will make it 8-bytes no matter what
- ptr arrs[];
-} bh__table;
-
-#define bh_table(T) T*
-
-#ifdef BH_TABLE_SIZE_SAFE
- #define bh_table_init(allocator_, tab, hs) bh__table_init(allocator_, (bh__table **)&(tab), hs)
- #define bh_table_free(tab) bh__table_free((bh__table **)&(tab))
- #define bh_table_put(T, tab, key, value) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__table_put((bh__table *) tab, sizeof(T), key)) = (T) value))
- #define bh_table_has(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (bh__table_has((bh__table *) tab, sizeof(T), key)))
- #define bh_table_get(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__table_get((bh__table *) tab, sizeof(T), key))))
- #define bh_table_delete(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), bh__table_delete((bh__table *) tab, sizeof(T), key))
- #define bh_table_clear(tab) (bh__table_clear((bh__table *) tab))
-
- #define bh_table_iter_setup(T, tab) (assert(sizeof(T) == sizeof(*(tab))), bh__table_iter_setup((bh__table *) tab, sizeof(T)))
- #define bh_table_iter_key(it) ((char *)(bh_pointer_add(it.entry, it.elemsize + sizeof(u16))))
- #define bh_table_iter_value(T, it) (*(T *)it.entry)
-#else
- #define bh_table_init(allocator_, tab, hs) bh__table_init(allocator_, (bh__table **)&(tab), hs)
- #define bh_table_free(tab) bh__table_free((bh__table **)&(tab))
- #define bh_table_put(T, tab, key, value) (*((T *) bh__table_put((bh__table *) tab, sizeof(T), key)) = value)
- #define bh_table_has(T, tab, key) (bh__table_has((bh__table *) tab, sizeof(T), key))
- #define bh_table_get(T, tab, key) (*((T *) bh__table_get((bh__table *) tab, sizeof(T), key)))
- #define bh_table_delete(T, tab, key) (bh__table_delete((bh__table *) tab, sizeof(T), key))
- #define bh_table_clear(tab) (bh__table_clear((bh__table *) tab))
-
- #define bh_table_iter_setup(T, tab) (bh__table_iter_setup((bh__table *) tab, sizeof(T)))
- #define bh_table_iter_key(it) ((char *)(bh_pointer_add(it.entry, it.elemsize + sizeof(u16))))
- #define bh_table_iter_value(T, it) (*(T *)it.entry)
-#endif
-
-#define bh_table_each_start(T, table) { \
- bh_table_iterator it = bh_table_iter_setup(T, (table)); \
- while (bh_table_iter_next(&it)) { \
- const char* key = bh_table_iter_key(it); \
- T value = bh_table_iter_value(T, it);
-#define bh_table_each_end } }
-
-b32 bh__table_init(bh_allocator allocator, bh__table **table, i32 table_size);
-b32 bh__table_free(bh__table **table);
-ptr bh__table_put(bh__table *table, i32 elemsize, char *key);
-b32 bh__table_has(bh__table *table, i32 elemsize, char *key);
-ptr bh__table_get(bh__table *table, i32 elemsize, char *key);
-void bh__table_delete(bh__table *table, i32 elemsize, char *key);
-void bh__table_clear(bh__table *table);
-bh_table_iterator bh__table_iter_setup(bh__table *table, i32 elemsize);
-b32 bh_table_iter_next(bh_table_iterator* it);
-
-#endif
-// Using stb_ds for tables now because they are better in every single way.
-#define Table(T) struct { char *key; T value; } *
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------
-// IMAP (integer to integer map)
-//-------------------------------------------------------------------------------
-#ifndef BH_NO_IMAP
-
-typedef u64 bh_imap_entry_t;
-
-typedef struct bh__imap_lookup_result {
- i64 hash_index;
- i64 entry_prev;
- i64 entry_index;
-} bh__imap_lookup_result;
-
-typedef struct bh__imap_entry {
- bh_imap_entry_t key, value;
- i64 next;
-} bh__imap_entry;
-
-typedef struct bh_imap {
- bh_allocator allocator;
-
- bh_arr(i64) hashes;
- bh_arr(bh__imap_entry) entries;
-} bh_imap;
-
-
-void bh_imap_init(bh_imap* imap, bh_allocator alloc, i32 hash_count);
-void bh_imap_free(bh_imap* imap);
-void bh_imap_put(bh_imap* imap, bh_imap_entry_t key, bh_imap_entry_t value);
-b32 bh_imap_has(bh_imap* imap, bh_imap_entry_t key);
-bh_imap_entry_t bh_imap_get(bh_imap* imap, bh_imap_entry_t key);
-void bh_imap_delete(bh_imap* imap, bh_imap_entry_t key);
-void bh_imap_clear(bh_imap* imap);
-
-#ifdef BH_DEFINE
-#endif // BH_DEFINE
-
-
-#endif
-
-
-
-
-
-
-
-
-
-
-
-// MANAGED HEAP ALLOCATOR
-typedef struct bh_managed_heap {
- bh_imap ptrs;
-} bh_managed_heap;
-
-void bh_managed_heap_init(bh_managed_heap* mh);
-void bh_managed_heap_free(bh_managed_heap* mh);
-bh_allocator bh_managed_heap_allocator(bh_managed_heap* mh);
-BH_ALLOCATOR_PROC(bh_managed_heap_allocator_proc);
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------
-// OTHER COMMON DATA STRUCTURES
-//-------------------------------------------------------------------------------
-#ifndef BH_NO_DATASTRUCTURES
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-#endif // BH_NO_DATASTRUCTURES
-
-
-
-
-
-//------------------------------------------------------------------------------
-// TIME / DURATION
-//------------------------------------------------------------------------------
-u64 bh_time_curr();
-u64 bh_time_duration(u64 old);
-
-
-
-
-
-
-
-
-
-
-
-#ifdef BH_DEFINE
-
-#undef BH_DEFINE
-//-------------------------------------------------------------------------------------
-// IMPLEMENTATIONS
-//-------------------------------------------------------------------------------------
-
-//-------------------------------------------------------------------------------------
-// CHAR FUNCTIONS
-//-------------------------------------------------------------------------------------
-
-b32 char_is_alpha(const char a) {
- return ('a' <= a && a <= 'z') || ('A' <= a && a <= 'Z');
-}
-
-char charset_contains(const char* charset, char ch) {
- while (*charset) {
- if (*charset == ch) return ch;
- charset++;
- }
-
- return 0;
-}
-
-b32 char_is_num(const char a) {
- return ('0' <= a && a <= '9');
-}
-
-b32 char_is_alphanum(const char a) {
- return char_is_alpha(a) || char_is_num(a);
-}
-
-b32 char_is_whitespace(const char a) {
- return charset_contains(" \t\r\n", a);
-}
-
-b32 char_in_range(const char lo, const char hi, const char a) {
- return lo <= a && a <= hi;
-}
-
-i64 chars_match(char* ptr1, char* ptr2) {
- i64 len = 0;
- while (*ptr2 != '\0' && *ptr1 == *ptr2) ptr1++, ptr2++, len++;
- return *ptr2 == '\0' ? len : 0;
-}
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// CUSTOM ALLOCATORS IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-ptr bh_alloc(bh_allocator a, isize size) {
- return bh_alloc_aligned(a, size, 16);
-}
-
-ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment) {
- return a.proc(a.data, bh_allocator_action_alloc, size, alignment, NULL, 0);
-}
-
-ptr bh_resize(bh_allocator a, ptr data, isize new_size) {
- return bh_resize_aligned(a, data, new_size, 16);
-}
-
-ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment) {
- return a.proc(a.data, bh_allocator_action_resize, new_size, alignment, data, 0);
-}
-
-void bh_free(bh_allocator a, ptr data) {
- if (data != NULL) a.proc(a.data, bh_allocator_action_free, 0, 0, data, 0);
-}
-
-
-
-// HEAP ALLOCATOR IMPLEMENTATION
-bh_allocator bh_heap_allocator(void) {
- return (bh_allocator) {
- .proc = bh_heap_allocator_proc,
- .data = NULL
- };
-}
-
-BH_ALLOCATOR_PROC(bh_heap_allocator_proc) {
- ptr retval = NULL;
-
- switch (action) {
- case bh_allocator_action_alloc: {
-#if defined(_BH_WINDOWS)
- retval = _aligned_malloc(size, alignment);
-#elif defined(_BH_LINUX)
- i32 success = posix_memalign(&retval, alignment, size);
-#endif
- if (flags & bh_allocator_flag_clear && retval != NULL) {
- memset(retval, 0, size);
- }
- } break;
-
- case bh_allocator_action_resize: {
- // TODO: Maybe replace with better custom function
-#if defined(_BH_WINDOWS)
- retval = _aligned_realloc(prev_memory, size, alignment);
-#elif defined(_BH_LINUX)
- retval = realloc(prev_memory, size);
-#endif
- } break;
-
- case bh_allocator_action_free: {
-#if defined(_BH_WINDOWS)
- _aligned_free(prev_memory);
-#elif defined(_BH_LINUX)
- free(prev_memory);
-#endif
- } break;
- }
-
- return retval;
-}
-
-
-
-
-
-
-// MANAGED HEAP ALLOCATOR IMPLEMENTATION
-void bh_managed_heap_init(bh_managed_heap* mh) {
- bh_imap_init(&mh->ptrs, bh_heap_allocator(), 512);
-}
-
-void bh_managed_heap_free(bh_managed_heap* mh) {
- bh_arr_each(bh__imap_entry, p, mh->ptrs.entries) {
-#if defined(_BH_WINDOWS)
- _aligned_free((void *) p->key);
-#elif defined(_BH_LINUX)
- free((void *) p->key);
-#endif
- }
-
- bh_imap_free(&mh->ptrs);
-}
-
-bh_allocator bh_managed_heap_allocator(bh_managed_heap* mh) {
- return (bh_allocator) {
- .proc = bh_managed_heap_allocator_proc,
- .data = mh
- };
-}
-
-BH_ALLOCATOR_PROC(bh_managed_heap_allocator_proc) {
- bh_managed_heap* mh = (bh_managed_heap *) data;
- ptr retval = NULL;
-
- switch (action) {
- case bh_allocator_action_alloc: {
-#if defined(_BH_WINDOWS)
- retval = _aligned_malloc(size, alignment);
-#elif defined(_BH_LINUX)
- i32 success = posix_memalign(&retval, alignment, size);
-#endif
-
- if (flags & bh_allocator_flag_clear && retval != NULL) {
- memset(retval, 0, size);
- }
-
- if (retval != NULL)
- bh_imap_put(&mh->ptrs, (u64) retval, 1);
- } break;
-
- case bh_allocator_action_resize: {
- bh_imap_delete(&mh->ptrs, (u64) prev_memory);
-#if defined(_BH_WINDOWS)
- retval = _aligned_realloc(prev_memory, size, alignment);
-#elif defined(_BH_LINUX)
- retval = realloc(prev_memory, size);
-#endif
- bh_imap_put(&mh->ptrs, (u64) retval, 1);
- } break;
-
- case bh_allocator_action_free: {
- bh_imap_delete(&mh->ptrs, (u64) prev_memory);
-#if defined(_BH_WINDOWS)
- _aligned_free(prev_memory);
-#elif defined(_BH_LINUX)
- free(prev_memory);
-#endif
- } break;
- }
-
- return retval;
-}
-
-
-
-
-
-
-
-
-// ARENA ALLOCATOR IMPLEMENTATION
-void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size) {
- arena_size = bh_max(arena_size, size_of(ptr));
- ptr data = bh_alloc(backing, arena_size);
-
- alloc->backing = backing;
- alloc->arena_size = arena_size;
- alloc->size = sizeof(ptr);
- alloc->first_arena = data;
- alloc->current_arena = data;
-
- ((bh__arena_internal *)(alloc->first_arena))->next_arena = NULL;
-}
-
-void bh_arena_free(bh_arena* alloc) {
- bh__arena_internal *walker = (bh__arena_internal *) alloc->first_arena;
- bh__arena_internal *trailer = walker;
- while (walker != NULL) {
- walker = walker->next_arena;
- bh_free(alloc->backing, trailer);
- trailer = walker;
- }
-
- alloc->first_arena = NULL;
- alloc->current_arena = NULL;
- alloc->arena_size = 0;
- alloc->size = 0;
-}
-
-bh_allocator bh_arena_allocator(bh_arena* alloc) {
- return (bh_allocator) {
- .proc = bh_arena_allocator_proc,
- .data = alloc,
- };
-}
-
-BH_ALLOCATOR_PROC(bh_arena_allocator_proc) {
- bh_arena* alloc_arena = (bh_arena*) data;
-
- ptr retval = NULL;
-
- switch (action) {
- case bh_allocator_action_alloc: {
- bh_align(size, alignment);
- bh_align(alloc_arena->size, alignment);
-
- if (size > alloc_arena->arena_size - size_of(ptr)) {
- // Size too large for the arena
- return NULL;
- }
-
- if (alloc_arena->size + size >= alloc_arena->arena_size) {
- bh__arena_internal* new_arena = (bh__arena_internal *) bh_alloc(alloc_arena->backing, alloc_arena->arena_size);
-
- if (new_arena == NULL) {
- bh_printf_err("Arena Allocator: couldn't allocate new arena");
- return NULL;
- }
-
- new_arena->next_arena = NULL;
- ((bh__arena_internal *)(alloc_arena->current_arena))->next_arena = new_arena;
- alloc_arena->current_arena = new_arena;
- alloc_arena->size = sizeof(ptr);
- }
-
- retval = bh_pointer_add(alloc_arena->current_arena, alloc_arena->size);
- alloc_arena->size += size;
- } break;
-
- case bh_allocator_action_resize: {
- // Do nothing since this is a fixed allocator
- } break;
-
- case bh_allocator_action_free: {
- // Do nothing since this allocator isn't made for freeing memory
- } break;
- }
-
- return retval;
-}
-
-
-
-
-// SCRATCH ALLOCATOR IMPLEMENTATION
-void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size) {
- ptr memory = bh_alloc(backing, scratch_size);
-
- scratch->backing = backing;
- scratch->memory = memory;
- scratch->curr = memory;
- scratch->end = bh_pointer_add(memory, scratch_size);
-}
-
-void bh_scratch_free(bh_scratch* scratch) {
- bh_free(scratch->backing, scratch->memory);
-
- scratch->memory = NULL;
- scratch->curr = NULL;
- scratch->end = NULL;
-}
-
-bh_allocator bh_scratch_allocator(bh_scratch* scratch) {
- return (bh_allocator) {
- .proc = bh_scratch_allocator_proc,
- .data = scratch,
- };
-}
-
-BH_ALLOCATOR_PROC(bh_scratch_allocator_proc) {
- bh_scratch* scratch = (bh_scratch*) data;
- ptr retval = NULL;
-
- switch (action) {
- case bh_allocator_action_alloc: {
- if (size > ((u8 *) scratch->end) - ((u8 *) scratch->memory)) {
- return NULL;
- }
-
- retval = scratch->curr;
- scratch->curr = bh_pointer_add(scratch->curr, size);
-
- if (scratch->curr >= scratch->end) {
- scratch->curr = scratch->memory;
- retval = scratch->curr;
- }
- } break;
-
- case bh_allocator_action_free: break;
-
- case bh_allocator_action_resize: {
- if (size > ((u8 *) scratch->end) - ((u8 *) scratch->memory)) {
- return NULL;
- }
-
- retval = scratch->curr;
- scratch->curr = bh_pointer_add(scratch->curr, size);
-
- if (scratch->curr >= scratch->end) {
- scratch->curr = scratch->memory;
- retval = scratch->curr;
- }
-
- // HACK!!!!!: Using size instead of some kind of "old size"
- memcpy(retval, prev_memory, size);
- } break;
- }
-
- return retval;
-}
-
-
-
-
-//-------------------------------------------------------------------------------------
-// CONVERSION FUNCTIONS IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-u8* uint_to_uleb128(u64 n, i32* output_length) {
- static u8 buffer[16];
-
- *output_length = 0;
- u8* output = buffer;
- u8 byte;
- do {
- byte = n & 0x7f;
- n >>= 7;
- if (n != 0) byte |= (1 << 7);
- *output++ = byte;
- (*output_length)++;
- } while (n != 0);
-
- return buffer;
-}
-
-
-// Converts a signed integer to the signed LEB128 format
-u8* int_to_leb128(i64 n, i32* output_length) {
- static u8 buffer[16];
-
- *output_length = 0;
- u8* output = buffer;
- b32 more = 1;
-
- i32 size = 64;
-
- u8 byte;
- do {
- byte = n & 0x7f;
- n >>= 7;
-
- more = !(((n == 0) && (byte & 0x40) == 0) || ((n == -1) && (byte & 0x40) != 0));
- if (more)
- byte |= 0x80;
- *output++ = byte;
- (*output_length)++;
- } while (more);
-
- return buffer;
-}
-
-// NOTE: This assumes the underlying implementation of float on the host
-// system is already IEEE-754. This is safe to assume in most cases.
-u8* float_to_ieee754(f32 f, b32 reverse) {
- static u8 buffer[4];
-
- u8* fmem = (u8*) &f;
- if (reverse) {
- buffer[0] = fmem[3];
- buffer[1] = fmem[2];
- buffer[2] = fmem[1];
- buffer[3] = fmem[0];
- } else {
- buffer[0] = fmem[0];
- buffer[1] = fmem[1];
- buffer[2] = fmem[2];
- buffer[3] = fmem[3];
- }
-
- return buffer;
-}
-
-u8* double_to_ieee754(f64 f, b32 reverse) {
- static u8 buffer[8];
-
- u8* fmem = (u8*) &f;
- if (reverse) {
- buffer[0] = fmem[7];
- buffer[1] = fmem[6];
- buffer[2] = fmem[5];
- buffer[3] = fmem[4];
- buffer[4] = fmem[3];
- buffer[5] = fmem[2];
- buffer[6] = fmem[1];
- buffer[7] = fmem[0];
- } else {
- buffer[0] = fmem[0];
- buffer[1] = fmem[1];
- buffer[2] = fmem[2];
- buffer[3] = fmem[3];
- buffer[4] = fmem[4];
- buffer[5] = fmem[5];
- buffer[6] = fmem[6];
- buffer[7] = fmem[7];
- }
-
- return buffer;
-}
-
-u64 uleb128_to_uint(u8* bytes, i32 *byte_count) {
- u64 res = 0;
- u64 shift = 0;
-
- while (1) {
- u8 byte = bytes[(*byte_count)++];
- res |= (byte & 0x7f) << shift;
- if ((byte & 0x80) == 0) break;
- shift += 7;
- }
- return res;
-}
-
-
-
-//-------------------------------------------------------------------------------------
-// STRING IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-b32 bh_str_starts_with(char* str, char* start) {
- char* s = str;
- char* p = start;
-
- while (*s != '\0' && *p != '\0' && *s == *p) s++, p++;
-
- return *p == '\0';
-}
-
-b32 bh_str_ends_with(char* str, char* end) {
- i32 slen = strlen(str);
- i32 elen = strlen(end);
-
- char* s = str + slen - 1;
- char* e = end + elen - 1;
-
- while (*e == *s && e != end && s != str) e--, s--;
-
- return *e == *s;
-}
-
-char* bh_strdup(bh_allocator a, char* str) {
- u32 len = strlen(str);
- char* buf = bh_alloc(a, len + 1);
-
- char* t = buf;
- while ((*t++ = *str++));
- return buf;
-}
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// FILE IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_FILE
-
-static b32 bh__file_seek_wrapper(bh_file_descriptor fd, i64 offset, bh_file_whence whence, i64* new_offset);
-
-bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand) {
- const char* filename = NULL;
-
-#if defined(_BH_WINDOWS)
- bh_file_descriptor sd_fd;
-
- switch (stand) {
- case BH_FILE_STANDARD_INPUT:
- sd_fd = GetStdHandle(STD_INPUT_HANDLE);
- filename = "stdin";
- break;
- case BH_FILE_STANDARD_OUTPUT:
- sd_fd = GetStdHandle(STD_OUTPUT_HANDLE);
- filename = "stdout";
- break;
- case BH_FILE_STANDARD_ERROR:
- sd_fd = GetStdHandle(STD_ERROR_HANDLE);
- filename = "stderr";
- break;
- default:
- return BH_FILE_ERROR_BAD_FD;
- }
- file->fd = sd_fd;
-
-#elif defined(_BH_LINUX)
- i32 sd_fd = -1;
-
- switch (stand) {
- case BH_FILE_STANDARD_INPUT:
- sd_fd = STDIN_FILENO;
- filename = "stdin"; // These are constants in the data section so everything should be okay
- break;
- case BH_FILE_STANDARD_OUTPUT:
- sd_fd = STDOUT_FILENO;
- filename = "stdout";
- break;
- case BH_FILE_STANDARD_ERROR:
- sd_fd = STDERR_FILENO;
- filename = "stderr";
- break;
- default:
- return BH_FILE_ERROR_BAD_FD;
- }
-
- file->fd = sd_fd;
-#endif
-
-
- file->filename = filename;
- return BH_FILE_ERROR_NONE;
-}
-
-bh_file_error bh_file_create(bh_file* file, const char* filename) {
- // Need to do this to avoid compiler complaining about types
- bh_file_mode write_rw = (bh_file_mode) (BH_FILE_MODE_WRITE | BH_FILE_MODE_RW);
- return bh_file_open_mode(file, write_rw, filename);
-}
-
-bh_file_error bh_file_open(bh_file* file, const char* filename) {
- return bh_file_open_mode(file, BH_FILE_MODE_READ, filename);
-}
-
-bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename) {
-#if defined(_BH_WINDOWS)
- DWORD desired_access;
- DWORD creation_disposition;
-
- switch (mode & BH_FILE_MODE_MODES) {
- case BH_FILE_MODE_READ:
- desired_access = GENERIC_READ;
- creation_disposition = OPEN_EXISTING;
- break;
- case BH_FILE_MODE_WRITE:
- desired_access = GENERIC_WRITE;
- creation_disposition = CREATE_ALWAYS;
- break;
- case BH_FILE_MODE_APPEND:
- desired_access = GENERIC_WRITE;
- creation_disposition = OPEN_ALWAYS;
- break;
- case BH_FILE_MODE_READ | BH_FILE_MODE_RW:
- desired_access = GENERIC_READ | GENERIC_WRITE;
- creation_disposition = OPEN_EXISTING;
- break;
- case BH_FILE_MODE_WRITE | BH_FILE_MODE_RW:
- desired_access = GENERIC_READ | GENERIC_WRITE;
- creation_disposition = CREATE_ALWAYS;
- break;
- case BH_FILE_MODE_APPEND | BH_FILE_MODE_RW:
- desired_access = GENERIC_READ | GENERIC_WRITE;
- creation_disposition = OPEN_ALWAYS;
- break;
- default:
- return BH_FILE_ERROR_INVALID;
- }
-
-
- file->fd = CreateFileA(filename,
- desired_access,
- FILE_SHARE_READ,
- NULL,
- creation_disposition,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (file->fd == INVALID_HANDLE_VALUE) {
- return BH_FILE_ERROR_INVALID;
- }
-
- file->filename = filename;
- return BH_FILE_ERROR_NONE;
-
-#elif defined(_BH_LINUX)
- i32 os_mode = 0;
-
- switch (mode & BH_FILE_MODE_MODES) {
- case BH_FILE_MODE_READ: os_mode = O_RDONLY; break;
- case BH_FILE_MODE_WRITE: os_mode = O_WRONLY | O_CREAT | O_TRUNC; break;
- case BH_FILE_MODE_APPEND: os_mode = O_WRONLY | O_APPEND | O_CREAT; break;
- case BH_FILE_MODE_READ | BH_FILE_MODE_RW: os_mode = O_RDWR; break;
- case BH_FILE_MODE_WRITE | BH_FILE_MODE_RW: os_mode = O_RDWR | O_CREAT | O_TRUNC; break;
- case BH_FILE_MODE_APPEND | BH_FILE_MODE_RW: os_mode = O_RDWR | O_APPEND | O_CREAT; break;
- //default: // TODO Handle errors
- }
-
- file->fd = open(filename, os_mode,
- S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP //+rw-rw-rw-
- );
- if (file->fd < 0) {
- return BH_FILE_ERROR_INVALID;
- }
-
- // TODO: Set this using some allocator
- file->filename = filename;
-
- return BH_FILE_ERROR_NONE;
-#endif
-}
-
-bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename) {
- file->filename = filename; // This may be unsafe
- file->fd = fd;
- return BH_FILE_ERROR_NONE;
-}
-
-b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read) {
-#if defined(_BH_WINDOWS)
- bh_file_seek_to(file, offset);
- BOOL res = ReadFile(file->fd, buffer, buff_size, (i32 *) bytes_read, NULL);
- if (res) return 1;
- else return 0;
-
-#elif defined(_BH_LINUX)
- if (file->fd == 0) {
- isize res = read(file->fd, buffer, buff_size);
- if (res < 0) return 0;
- if (bytes_read) *bytes_read = res;
- return 1;
- }
-
- isize res = pread(file->fd, buffer, buff_size, offset);
- if (res < 0) return 0;
- if (bytes_read) *bytes_read = res;
- return 1;
-#endif
-}
-
-b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote) {
- isize res;
- i64 current_offset = 0;
- bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_CURRENT, ¤t_offset);
-
-#if defined(_BH_WINDOWS)
- if (current_offset != offset) bh_file_seek_to(file, offset);
- res = (isize) WriteFile(file->fd, buffer, buff_size, (i32 *) bytes_wrote, NULL);
- return res;
-
-#elif defined(_BH_LINUX)
- if (current_offset == offset || file->fd == 1 || file->fd == 2) {
- // Standard in and out do like pwrite()
- res = write(file->fd, buffer, buff_size);
- } else {
- res = pwrite(file->fd, buffer, buff_size, offset);
- }
- if (res < 0) return 0;
- if (bytes_wrote) *bytes_wrote = res;
-
- return 1;
-#endif
-}
-
-static b32 bh__file_seek_wrapper(bh_file_descriptor fd, i64 offset, bh_file_whence whence, i64* new_offset) {
-#if defined(_BH_WINDOWS)
- LARGE_INTEGER new_file_pointer;
- LARGE_INTEGER dest;
- dest.QuadPart = offset;
-
- BOOL res = SetFilePointerEx(fd, dest, &new_file_pointer, whence);
- *new_offset = new_file_pointer.QuadPart;
-
- return res;
-
-#elif defined(_BH_LINUX)
- i64 res = lseek64(fd, offset, whence);
- if (res < 0) return 0;
- if (new_offset) *new_offset = res;
- return 1;
-#endif
-}
-
-// Returns new offset
-i64 bh_file_seek(bh_file* file, i64 offset, bh_file_whence whence) {
- i64 new_offset = -1;
- bh__file_seek_wrapper(file->fd, offset, whence, &new_offset);
- return new_offset;
-}
-
-i64 bh_file_seek_to(bh_file* file, i64 offset) {
- i64 new_offset = -1;
- bh__file_seek_wrapper(file->fd, offset, BH_FILE_WHENCE_BEGIN, &new_offset);
- return new_offset;
-}
-
-i64 bh_file_seek_to_end(bh_file* file) {
- i64 new_offset = -1;
- bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_END, &new_offset);
- return new_offset;
-}
-
-i64 bh_file_skip(bh_file* file, i64 bytes) {
- i64 new_offset = 0;
- bh__file_seek_wrapper(file->fd, bytes, BH_FILE_WHENCE_CURRENT, &new_offset);
- return new_offset;
-}
-
-i64 bh_file_tell(bh_file* file) {
- i64 new_offset = 0;
- bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_CURRENT, &new_offset);
- return new_offset;
-}
-
-bh_file_error bh_file_close(bh_file* file) {
- bh_file_error err = BH_FILE_ERROR_NONE;
-
-#if defined(_BH_WINDOWS)
- BOOL success = CloseHandle(file->fd);
- if (!success) err = BH_FILE_ERROR_INVALID;
-
- return err;
-
-#elif defined(_BH_LINUX)
- i32 res = close(file->fd);
- if (res < 0)
- err = BH_FILE_ERROR_INVALID;
-
- return err;
-#endif
-}
-
-b32 bh_file_read(bh_file* file, void* buffer, isize buff_size) {
- return bh_file_read_at(file, bh_file_tell(file), buffer, buff_size, NULL);
-}
-
-b32 bh_file_write(bh_file* file, void* buffer, isize buff_size) {
- return bh_file_write_at(file, bh_file_tell(file), buffer, buff_size, NULL);
-}
-
-void bh_file_flush(bh_file* file) {
- #ifdef _BH_LINUX
- fdatasync(file->fd);
- #endif
-}
-
-i64 bh_file_size(bh_file* file) {
- i64 size = 0;
- i64 prev = bh_file_tell(file);
- bh_file_seek_to_end(file);
- size = bh_file_tell(file);
- bh_file_seek_to(file, prev);
- return size;
-}
-
-bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file) {
- bh_file_contents fc = {
- .allocator = alloc,
- .filename = bh_strdup(alloc, (char *) file->filename),
- .length = 0, .data = NULL, .line_count = 0,
- };
-
- isize size = bh_file_size(file);
- if (size <= 0) return fc;
-
- fc.data = bh_alloc(alloc, size + 1);
- fc.length = size;
- bh_file_read_at(file, 0, fc.data, fc.length, NULL);
- ((u8*) fc.data)[fc.length] = '\0';
-
- return fc;
-}
-
-bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename) {
- bh_file file;
- bh_file_open(&file, filename);
- bh_file_contents fc = bh_file_read_contents(alloc, &file);
- bh_file_close(&file);
- return fc;
-}
-
-b32 bh_file_contents_free(bh_file_contents* contents) {
- bh_free(contents->allocator, contents->data);
- contents->length = 0;
- return 1;
-}
-
-b32 bh_file_stat(char const* filename, bh_file_stats* out) {
- struct stat s;
- if (stat(filename, &s) == -1) {
- return 0;
- }
-
- out->size = s.st_size;
-
- if ((s.st_mode & S_IFMT) == S_IFDIR) out->file_type = BH_FILE_TYPE_DIRECTORY;
- if ((s.st_mode & S_IFMT) == S_IFREG) out->file_type = BH_FILE_TYPE_FILE;
-
-#if defined(_BH_LINUX)
- if ((s.st_mode & S_IFMT) == S_IFLNK) out->file_type = BH_FILE_TYPE_LINK;
-#endif
-
- return 1;
-}
-
-b32 bh_file_exists(char const* filename) {
- struct stat s;
- return stat(filename, &s) != -1;
-}
-
-b32 bh_file_remove(char const* filename) {
-#if defined(_BH_WINDOWS)
- return DeleteFileA(filename);
-
-#elif defined(_BH_LINUX)
- return unlink(filename) == 0;
-#endif
-}
-
-char* bh_path_get_full_name(char const* filename, bh_allocator a) {
-#if defined(_BH_WINDOWS)
- char buffer[4096];
- GetFullPathNameA(filename, 4096, buffer, NULL);
-
- i32 len = strlen(buffer);
- char* result = bh_alloc_array(a, char, len + 1);
- memmove(result, buffer, len);
- result[len] = 0;
-
- return result;
-
-#elif defined(_BH_LINUX)
- char* p = realpath(filename, NULL);
-
- // Check if the file did not exists.
- // :Cleanup should this return NULL?
- if (p == NULL) return (char *) filename;
-
- i32 len = strlen(p);
- char* result = bh_alloc_array(a, char, len + 1);
- memmove(result, p, len);
- result[len] = 0;
-
- free(p);
-
- return result;
-#endif
-}
-
-// NOTE: This assumes the filename is the full path, not relative to anything else.
-#if defined(_BH_LINUX)
- #define DIR_SEPARATOR '/'
-#elif defined(_BH_WINDOWS)
- #define DIR_SEPARATOR '\\'
-#endif
-char* bh_path_get_parent(char const* filename, bh_allocator a) {
-
- char* result = bh_strdup(a, (char *) filename);
- char* end = result + strlen(result);
- while (*end != DIR_SEPARATOR && end != result) *end-- = '\0';
-
- return result;
-}
-
-// This function returns a volatile pointer. Do not store it without copying!
-char* bh_lookup_file(char* filename, char* relative_to, char *suffix, b32 add_suffix, bh_arr(const char *) included_folders, b32 search_included_folders) {
- assert(relative_to != NULL);
-
- static char path[512];
- fori (i, 0, 512) path[i] = 0;
-
- static char fn[256];
- fori (i, 0, 256) fn[i] = 0;
-
- if (!bh_str_ends_with(filename, suffix) && add_suffix) {
- bh_snprintf(fn, 256, "%s%s", filename, suffix);
- } else {
- bh_snprintf(fn, 256, "%s", filename);
- }
-
- fori (i, 0, 256) 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, 512, "%s%c%s", relative_to, DIR_SEPARATOR, fn + 2);
- else
- bh_snprintf(path, 512, "%s%s", relative_to, fn + 2);
-
- if (bh_file_exists(path)) return bh_path_get_full_name(path, bh_heap_allocator());
-
- return fn;
- }
-
- if (search_included_folders) {
- bh_arr_each(const char *, folder, included_folders) {
- if ((*folder)[strlen(*folder) - 1] != DIR_SEPARATOR)
- bh_snprintf(path, 512, "%s%c%s", *folder, DIR_SEPARATOR, fn);
- else
- bh_snprintf(path, 512, "%s%s", *folder, fn);
-
- if (bh_file_exists(path)) return bh_path_get_full_name(path, bh_heap_allocator());
- }
- }
-
- return fn;
-}
-
-//
-// Modifies the path in-place.
-char* bh_path_convert_separators(char* path) {
-#if defined(_BH_LINUX)
- #define DIR_SEPARATOR '/'
- #define OTHER_SEPARATOR '\\'
-#elif defined(_BH_WINDOWS)
- #define DIR_SEPARATOR '\\'
- #define OTHER_SEPARATOR '/'
-#endif
-
- fori (i, 0, (i64) strlen(path)) {
- if (path[i] == OTHER_SEPARATOR) {
- path[i] = DIR_SEPARATOR;
- }
- }
-
- return path;
-}
-
-
-bh_dir bh_dir_open(char* path) {
-#ifdef _BH_WINDOWS
- char new_path[512] = { 0 };
- strncpy(new_path, path, 511);
- bh_path_convert_separators(new_path);
- strncat(new_path, "\\*.*", 511);
-
- Windows_Directory_Opened* dir = malloc(sizeof(Windows_Directory_Opened));
- dir->hndl = FindFirstFileA(new_path, &dir->found_file);
- if (dir->hndl == INVALID_HANDLE_VALUE) {
- return NULL;
- }
-
- return dir;
-#endif
-
-#ifdef _BH_LINUX
- DIR* dir = opendir(path);
- return dir;
-#endif
-}
-
-b32 bh_dir_read(bh_dir dir, bh_dirent* out) {
-
-#ifdef _BH_WINDOWS
- do {
- BOOL success = FindNextFileA(dir->hndl, &dir->found_file);
- if (!success) return 0;
- } while (!strcmp(dir->found_file.cFileName, ".") || !strcmp(dir->found_file.cFileName, ".."));
-
- if (out == NULL) return 1;
-
- out->type = (dir->found_file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
- ? BH_DIRENT_DIRECTORY : BH_DIRENT_FILE;
- out->id = 0;
- out->name_length = strlen(dir->found_file.cFileName);
- strncpy(out->name, dir->found_file.cFileName, 256);
-
- return 1;
-#endif
-
-#ifdef _BH_LINUX
- struct dirent *ent;
- while (1) {
- ent = readdir(dir);
- if (ent == NULL) return 0;
-
- // Skip the current directory and parent directory
- if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) break;
- }
-
- bh_dirent_type type = 0;
- switch (ent->d_type) {
- case DT_UNKNOWN: break;
- case DT_BLK: type = BH_DIRENT_BLOCK; break;
- case DT_CHR: type = BH_DIRENT_CHAR; break;
- case DT_DIR: type = BH_DIRENT_DIRECTORY; break;
- case DT_LNK: type = BH_DIRENT_SYMLINK; break;
- case DT_REG: type = BH_DIRENT_FILE; break;
- default: type = BH_DIRENT_OTHER; break;
- }
-
- if (out == NULL) return 1;
-
- out->type = type;
- out->id = (u32) ent->d_ino;
- out->name_length = strlen(ent->d_name);
- strncpy(out->name, ent->d_name, 256);
-
- return 1;
-#endif
-}
-
-void bh_dir_close(bh_dir dir) {
-#ifdef _BH_WINDOWS
- if (dir == NULL) return;
-
- FindClose(dir->hndl);
- free(dir);
-#endif
-
-#ifdef _BH_LINUX
- if (dir == NULL) return;
- closedir(dir);
-#endif
-}
-
-#undef DIR_SEPARATOR
-
-#endif // ifndef BH_NO_FILE
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// ALTERNATE PRINTF IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-isize bh_printf(char const *fmt, ...) {
- isize res;
- va_list va;
- va_start(va, fmt);
- res = bh_printf_va(fmt, va);
- va_end(va);
- return res;
-}
-
-isize bh_printf_va(char const *fmt, va_list va) {
- bh_file file;
- bh_file_get_standard(&file, BH_FILE_STANDARD_OUTPUT);
- return bh_fprintf_va(&file, fmt, va);
-}
-
-isize bh_printf_err(char const *fmt, ...) {
- isize res;
- va_list va;
- va_start(va, fmt);
- res = bh_printf_err_va(fmt, va);
- va_end(va);
- return res;
-}
-
-isize bh_printf_err_va(char const *fmt, va_list va) {
- bh_file file;
- bh_file_get_standard(&file, BH_FILE_STANDARD_ERROR);
- return bh_fprintf_va(&file, fmt, va);
-}
-
-isize bh_fprintf(bh_file* f, char const *fmt, ...) {
- isize res;
- va_list va;
- va_start(va, fmt);
- res = bh_fprintf_va(f, fmt, va);
- va_end(va);
- return res;
-}
-
-isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va) {
- static char buffer[4096];
- isize len = bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
- bh_file_write(f, buffer, len - 1);
- return len;
-}
-
-char* bh_bprintf(char const *fmt, ...) {
- char* res;
- va_list va;
- va_start(va, fmt);
- res = bh_bprintf_va(fmt, va);
- va_end(va);
- return res;
-}
-
-char* bh_bprintf_va(char const *fmt, va_list va) {
- static char buffer[4096];
- isize len = bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
- buffer[len - 1] = 0;
- return buffer;
-}
-
-char* bh_aprintf(bh_allocator alloc, const char* fmt, ...) {
- char* res;
- va_list va;
- va_start(va, fmt);
- res = bh_aprintf_va(alloc, fmt, va);
- va_end(va);
- return res;
-}
-
-char* bh_aprintf_va(bh_allocator alloc, const char* fmt, va_list va) {
- static char buffer[4096];
- isize len = bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
- char* res = bh_alloc(alloc, len);
- memcpy(res, buffer, len);
- res[len - 1] = 0;
- return res;
-}
-
-isize bh_snprintf(char *str, isize n, char const *fmt, ...) {
- isize res;
- va_list va;
- va_start(va, fmt);
- res = bh_snprintf_va(str, n, fmt, va);
- va_end(va);
- return res;
-}
-
-isize bh__print_string(char* dest, isize n, char* src) {
- isize len = 0;
- while (n-- && (*dest++ = *src++)) len++;
- return len;
-}
-
-isize bh__printu64(char* str, isize n, bh__print_format format, u64 value) {
- char buf[128];
- buf[127] = 0;
- char* walker = buf + 127;
- u32 base = format.base ? format.base : 10, tmp;
-
- while (value > 0) {
- tmp = value % base;
- if (tmp > 9) {
- switch (tmp) {
- case 10: tmp = 'a'; break;
- case 11: tmp = 'b'; break;
- case 12: tmp = 'c'; break;
- case 13: tmp = 'd'; break;
- case 14: tmp = 'e'; break;
- case 15: tmp = 'f'; break;
- }
- } else {
- tmp += '0';
- }
-
- *--walker = tmp;
- value /= base;
- }
-
- if (format.base == 16) {
- *--walker = 'x';
- *--walker = '0';
- }
-
- return bh__print_string(str, n, walker);
-}
-
-isize bh__printi64(char* str, isize n, bh__print_format format, i64 value) {
- char buf[128];
- buf[127] = 0;
- char* walker = buf + 127;
- u32 base = format.base ? format.base : 10, tmp;
-
- b32 negative = value < 0 ? 1 : 0;
- if (negative) value = -value;
-
- if (value == 0) {
- *--walker = '0';
- } else {
- while (value > 0) {
- tmp = value % base;
- if (tmp > 9) {
- switch (tmp) {
- case 10: tmp = 'a'; break;
- case 11: tmp = 'b'; break;
- case 12: tmp = 'c'; break;
- case 13: tmp = 'd'; break;
- case 14: tmp = 'e'; break;
- case 15: tmp = 'f'; break;
- }
- } else {
- tmp += '0';
- }
-
- *--walker = tmp;
- value /= base;
- }
- }
-
- if (negative) {
- *--walker = '-';
- }
-
- if (format.base == 16) {
- *--walker = 'x';
- *--walker = '0';
- }
-
- return bh__print_string(str, n, walker);
-}
-
-// TODO: This implementation is VERY VERY BAD AND WRONG. Fix it.
-isize bh__printf64(char* str, isize n, f64 value) {
- fori (i, 0, 6) value *= 10.0;
- i64 v = (i64) value;
-
- isize l1 = bh__printi64(str, n, ((bh__print_format) { .base = 10 }), v / 1000000);
- str += l1;
- n -= l1;
-
- *str = '.';
- str += 1;
- n -= 1;
-
- isize l2 = bh__printi64(str, n, ((bh__print_format) { .base = 10 }), bh_abs(v) % 1000000);
-
- return l1 + l2 + 1;
-}
-
-// TODO: This is very hacked together but for now it will work.
-isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va) {
- char const *text_start = str;
- isize res;
-
- while (*fmt) {
- bh__print_format format = { 0 };
- isize len = 0;
-
- while (*fmt && *fmt != '%') {
- *(str++) = *(fmt++);
- }
-
- if (!*fmt) goto end_of_format;
-
- fmt++;
-
- switch (*fmt++) {
- case 'o': format.base = 8; break;
- case 'x': format.base = 16; break;
- default: fmt--;
- }
-
- switch (*fmt) {
- case 'c': {
- char c = (char) va_arg(va, int);
- *(str++) = c;
- } break;
-
- case 'd': {
- i64 value = (i64) va_arg(va, int);
- len = bh__printi64(str, n, format, value);
- } break;
-
- case 'l': {
- i64 value = (i64) va_arg(va, long);
- len = bh__printi64(str, n, format, value);
- } break;
-
- case 'p': {
- u64 value = (u64) va_arg(va, ptr);
- format.base = 16;
- len = bh__printu64(str, n, format, value);
- } break;
-
- case 's': {
- char* s = va_arg(va, char *);
- len = bh__print_string(str, n, s);
- } break;
-
- case 'b': { // String with a length (not null terminated)
- char* s = va_arg(va, char *);
- i32 l = va_arg(va, int);
- len = bh__print_string(str, bh_min(l, n), s);
- } break;
-
- case 'f': {
- f64 f = va_arg(va, f64);
- len = bh__printf64(str, n, f);
- } break;
-
- default: fmt--;
- }
-
- fmt++;
-
-end_of_format:
- str += len;
- n -= len;
- }
-
- return str - text_start + 1;
-}
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// FLEXIBLE BUFFER IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_BUFFER
-
-void bh_buffer_init(bh_buffer* buffer, bh_allocator alloc, i32 init_size) {
- buffer->allocator = alloc;
- buffer->length = 0;
- buffer->capacity = init_size;
- buffer->data = bh_alloc(alloc, init_size);
-}
-
-void bh_buffer_free(bh_buffer* buffer) {
- bh_free(buffer->allocator, buffer->data);
- buffer->length = 0;
- buffer->capacity = 0;
-}
-
-void bh_buffer_clear(bh_buffer* buffer) {
- buffer->length = 0;
-}
-
-void bh_buffer_grow(bh_buffer* buffer, i32 length) {
- if (buffer == NULL) return;
-
- if (buffer->capacity >= length) {
- // NOTE: Already have enough room
- return;
- }
-
- i32 newcap = buffer->capacity;
- while (newcap < length) newcap = BH_BUFFER_GROW_FORMULA(newcap);
-
- ptr new_data = bh_resize(buffer->allocator, buffer->data, newcap);
- if (new_data == NULL) return;
-
- buffer->capacity = newcap;
- buffer->data = new_data;
-}
-
-void bh_buffer_append(bh_buffer* buffer, const void * data, i32 length) {
- if (buffer == NULL) return;
-
- if (buffer->length + length > buffer->capacity) {
- bh_buffer_grow(buffer, buffer->length + length);
- }
-
- memcpy(buffer->data + buffer->length, data, length);
- buffer->length += length;
-}
-
-void bh_buffer_concat(bh_buffer* buffer, bh_buffer other) {
- bh_buffer_append(buffer, other.data, other.length);
-}
-
-void bh_buffer_write_byte(bh_buffer* buffer, u8 byte) {
- bh_buffer_grow(buffer, buffer->length + 1);
- buffer->data[buffer->length++] = byte;
-}
-
-void bh_buffer_write_u32(bh_buffer* buffer, u32 i) {
- bh_buffer_grow(buffer, buffer->length + 4);
- *((u32 *) bh_pointer_add(buffer->data, buffer->length)) = i;
- buffer->length += 4;
-}
-
-void bh_buffer_write_u64(bh_buffer* buffer, u64 i) {
- bh_buffer_grow(buffer, buffer->length + 8);
- *((u64 *) bh_pointer_add(buffer->data, buffer->length)) = i;
- buffer->length += 8;
-}
-
-void bh_buffer_align(bh_buffer* buffer, u32 alignment) {
- if (buffer->length % alignment != 0) {
- u32 difference = alignment - (buffer->length % alignment);
- buffer->length += difference;
-
- bh_buffer_grow(buffer, buffer->length);
- }
-}
-
-
-#endif
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// ARRAY IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_ARRAY
-
-b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap) {
- bh__arr* arrptr;
-
- if (*arr == NULL) {
- if (cap == 0 && elemsize == 0) return 1;
-
- arrptr = (bh__arr *) bh_alloc(alloc, sizeof(*arrptr) + elemsize * cap);
- if (arrptr == NULL) return 0;
-
- arrptr->allocator = alloc;
- arrptr->capacity = cap;
- arrptr->length = 0;
-
- } else {
- arrptr = bh__arrhead(*arr);
-
- if (arrptr->capacity < cap) {
- void* p;
- i32 newcap = arrptr->capacity;
- while (newcap < cap) newcap = BH_ARR_GROW_FORMULA(newcap);
-
- p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * newcap);
-
- if (p) {
- arrptr = (bh__arr *) p;
- arrptr->capacity = newcap;
- } else {
- return 0;
- }
- }
- }
-
- *arr = arrptr + 1;
- return 1;
-}
-
-b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap) {
- if (*arr == NULL) return 0;
-
- bh__arr* arrptr = bh__arrhead(*arr);
- cap = bh_max(cap, arrptr->length);
-
- if (arrptr->capacity > cap) {
- void* p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * cap);
-
- if (p) {
- arrptr = (bh__arr *) p;
- arrptr->capacity = cap;
- } else {
- return 0;
- }
- }
-
- *arr = arrptr + 1;
- return 1;
-}
-
-b32 bh__arr_free(void **arr) {
- if (*arr == NULL) return 0;
-
- bh__arr* arrptr = bh__arrhead(*arr);
- bh_free(arrptr->allocator, arrptr);
- *arr = NULL;
- return 1;
-}
-
-void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize) {
- bh__arr* arrptr = bh__arrhead(arr);
-
- const i32 cap = arrptr->length;
-
- void* newarr = NULL;
- bh__arr_grow(alloc, &newarr, elemsize, cap);
- bh__arrhead(newarr)->length = cap;
- bh__arrhead(newarr)->capacity = cap;
- memcpy(newarr, arr, elemsize * arrptr->length);
-
- return newarr;
-}
-
-void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems) {
- bh__arr* arrptr = bh__arrhead(*arr);
-
- if (index >= arrptr->length) return; // Can't delete past the end of the array
- if (numelems <= 0) return; // Can't delete nothing
-
- memmove(
- (char *)(*arr) + elemsize * index, // Target
- (char *)(*arr) + elemsize * (index + numelems), // Source
- elemsize * (arrptr->length - (index + numelems))); // Length
- arrptr->length -= numelems;
-}
-
-void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems) {
- if (numelems) {
- if (*arr == NULL) {
- bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, numelems); // Making a new array
- return;
- }
-
- bh__arr* arrptr = bh__arrhead(*arr);
- if (!bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, arrptr->length + numelems)) return; // Fail case
- arrptr = bh__arrhead(*arr);
- memmove(
- (char *)(*arr) + elemsize * (index + numelems),
- (char *)(*arr) + elemsize * index,
- elemsize * (arrptr->length - index));
- arrptr->length += numelems;
- }
-}
-
-#endif // ifndef BH_NO_ARRAY
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//-------------------------------------------------------------------------------------
-// TABLE IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_TABLE
-
-b32 bh__table_init(bh_allocator allocator, bh__table **table, i32 table_size) {
- *table = bh_alloc(allocator, sizeof(bh__table) + sizeof(ptr) * table_size);
- if (*table == NULL) return 0;
-
- (*table)->allocator = allocator;
- (*table)->table_size = table_size;
-
- for (i32 i = 0; i < table_size; i++) {
- (*table)->arrs[i] = NULL;
- }
-
- return 1;
-}
-
-b32 bh__table_free(bh__table **table) {
- if (*table == NULL) return 0;
-
- for (u64 i = 0; i < (*table)->table_size; i++) {
- if ((*table)->arrs[i] != NULL) {
- bh_arr_free((*table)->arrs[i]);
- }
- }
-
- bh_free((*table)->allocator, *table);
- *table = NULL;
- return 1;
-}
-
-// Assumes NULL terminated string for key
-ptr bh__table_put(bh__table *table, i32 elemsize, char *key) {
- elemsize += (elemsize & 1);
-
- u64 index = bh__table_hash_function(key, 0, table->table_size);
- u8 arr_was_new = 0;
-
- ptr arrptr = table->arrs[index];
- if (arrptr == NULL) {
- arr_was_new = 1;
- goto add_new_element;
- }
- u64 len = *(u64 *) arrptr;
- arrptr = bh_pointer_add(arrptr, sizeof(u64));
-
- u16 key_length = 0;
- while (len--) {
- arrptr = bh_pointer_add(arrptr, elemsize);
- key_length = *(u16 *) arrptr;
- arrptr = bh_pointer_add(arrptr, sizeof(u16));
- if (strncmp(key, (char *) arrptr, key_length) == 0) goto found_matching;
- arrptr = bh_pointer_add(arrptr, key_length);
- }
-
-add_new_element:
- arrptr = table->arrs[index];
- i32 byte_len = bh_arr_length(arrptr);
- if (byte_len == 0) byte_len = sizeof(u64);
- key_length = strlen(key) + 1;
-
- // NOTE: Align to 16 bytes
- if ((key_length + 2) % 16 != 0) {
- key_length = ((((key_length + 2) >> 4) + 1) << 4) - 2;
- }
-
- bh__arr_grow(table->allocator, &arrptr, 1, byte_len + elemsize + sizeof(u16) + key_length);
- bh__arrhead(arrptr)->length = byte_len + elemsize + sizeof(u16) + key_length;
- table->arrs[index] = arrptr;
-
- if (arr_was_new) {
- *(u64 *) arrptr = 1;
- } else {
- (*(u64 *) arrptr)++;
- }
-
- arrptr = bh_pointer_add(arrptr, byte_len + elemsize);
- *(u16 *) arrptr = key_length;
- arrptr = bh_pointer_add(arrptr, sizeof(u16));
- strncpy(arrptr, key, key_length);
-
-found_matching:
- return bh_pointer_add(arrptr, -(sizeof(u16) + elemsize));
-}
-
-b32 bh__table_has(bh__table *table, i32 elemsize, char *key) {
- elemsize += (elemsize & 1);
-
- u64 index = bh__table_hash_function(key, 0, table->table_size);
-
- ptr arrptr = table->arrs[index];
- if (arrptr == NULL) return 0;
-
- u64 len = *(u64 *) arrptr;
- arrptr = bh_pointer_add(arrptr, sizeof(u64));
-
- u16 key_length = 0;
- while (len--) {
- arrptr = bh_pointer_add(arrptr, elemsize);
- key_length = *(u16 *) arrptr;
- arrptr = bh_pointer_add(arrptr, sizeof(u16));
- if (strncmp(key, (char *) arrptr, key_length) == 0) return 1;
- arrptr = bh_pointer_add(arrptr, key_length);
- }
-
- return 0;
-}
-
-ptr bh__table_get(bh__table *table, i32 elemsize, char *key) {
- elemsize += (elemsize & 1);
-
- u64 index = bh__table_hash_function(key, 0, table->table_size);
-
- ptr arrptr = table->arrs[index];
- if (arrptr == NULL) return 0;
-
- u64 len = *(u64 *) arrptr;
- arrptr = bh_pointer_add(arrptr, sizeof(u64));
-
- u16 key_length = 0;
- while (len--) {
- arrptr = bh_pointer_add(arrptr, elemsize);
- key_length = *(u16 *) arrptr;
- arrptr = bh_pointer_add(arrptr, sizeof(u16));
- if (strncmp(key, (char *) arrptr, key_length) == 0) {
- return bh_pointer_add(arrptr, -(sizeof(u16) + elemsize));
- }
- arrptr = bh_pointer_add(arrptr, key_length);
- }
-
- return NULL;
-}
-
-void bh__table_delete(bh__table *table, i32 elemsize, char *key) {
- elemsize += (elemsize & 1);
-
- u64 index = bh__table_hash_function(key, 0, table->table_size);
-
- ptr arrptr = table->arrs[index], walker;
- if (arrptr == NULL) return; // Didn't exist
- walker = arrptr;
-
- i32 byte_offset = 8;
- i32 delete_len = 0;
-
- u64 len = *(u64 *) walker;
- walker = bh_pointer_add(walker, sizeof(u64));
-
- u16 key_length = 0;
- while (len--) {
- walker = bh_pointer_add(walker, elemsize);
- key_length = *(u16 *) walker;
- walker = bh_pointer_add(walker, sizeof(u16));
- if (strncmp(key, (char *) walker, key_length) == 0) {
- delete_len = elemsize + sizeof(u16) + key_length;
- goto found_matching;
- }
- walker = bh_pointer_add(walker, key_length);
- byte_offset += elemsize + sizeof(u16) + key_length;
- }
-
- // NOTE: Already didn't exist
- return;
-
-found_matching:
- bh__arr_deleten((void **) &arrptr, 1, byte_offset, delete_len);
- table->arrs[index] = arrptr;
- (*(u64 *) arrptr)--;
-}
-
-void bh__table_clear(bh__table *table) {
- for (u64 i = 0; i < table->table_size; i++) {
- if (table->arrs[i] != NULL) {
- // NOTE: Set length property to 0
- *((u64 *) table->arrs[i]) = 0;
- bh_arr_set_length(table->arrs[i], 0);
- }
- }
-}
-
-bh_table_iterator bh__table_iter_setup(bh__table *table, i32 elemsize) {
- elemsize += (elemsize & 1);
-
- bh_table_iterator it = {
- .tab = table->arrs,
- .endtab = table->arrs + table->table_size,
- .elemsize = elemsize,
- .entry = NULL
- };
- return it;
-}
-
-b32 bh_table_iter_next(bh_table_iterator* it) {
- if (it->tab == NULL) return 0;
-
- if (it->entry != NULL) {
- it->arrlen--;
- if (it->arrlen <= 0) {
- it->tab++;
- goto step_to_next;
- }
-
- it->entry = bh_pointer_add(it->entry, it->elemsize);
- it->entry = bh_pointer_add(it->entry, sizeof(u16) + (*(u16 *) it->entry));
- return 1;
- }
-
-step_to_next:
- // Step forward to find next valid
- while (it->tab != it->endtab && *it->tab == NULL) {
- it->tab++;
- }
-
- if (it->tab == it->endtab) return 0;
-
- it->entry = *it->tab;
- it->arrlen = *(u64 *) it->entry;
- it->entry = bh_pointer_add(it->entry, sizeof(u64));
- if (it->arrlen <= 0) {
- it->tab++;
- goto step_to_next;
- }
- return 1;
-}
-
-#endif // ifndef BH_NO_HASHTABLE
-
-
-
-//-------------------------------------------------------------------------------------
-// IMAP IMPLEMENTATION
-//-------------------------------------------------------------------------------------
-#ifndef BH_NO_IMAP
-void bh_imap_init(bh_imap* imap, bh_allocator alloc, i32 hash_count) {
- imap->allocator = alloc;
-
- imap->hashes = NULL;
- imap->entries = NULL;
-
- bh_arr_new(alloc, imap->hashes, hash_count);
- bh_arr_new(alloc, imap->entries, 4);
-
- fori(count, 0, hash_count) bh_arr_push(imap->hashes, -1);
-}
-
-void bh_imap_free(bh_imap* imap) {
- bh_arr_free(imap->hashes);
- bh_arr_free(imap->entries);
-
- imap->hashes = NULL;
- imap->entries = NULL;
-}
-
-bh__imap_lookup_result bh__imap_lookup(bh_imap* imap, bh_imap_entry_t key) {
- bh__imap_lookup_result lr = { -1, -1, -1 };
-
- u64 hash = 0xcbf29ce484222325ull ^ key;
- u64 n = bh_arr_capacity(imap->hashes);
-
- lr.hash_index = hash % n;
- lr.entry_index = imap->hashes[lr.hash_index];
- while (lr.entry_index >= 0) {
- if (imap->entries[lr.entry_index].key == key) {
- return lr;
- }
-
- lr.entry_prev = lr.entry_index;
- lr.entry_index = imap->entries[lr.entry_index].next;
- }
-
- return lr;
-}
-
-void bh_imap_put(bh_imap* imap, bh_imap_entry_t key, bh_imap_entry_t value) {
- bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
-
- if (lr.entry_index >= 0) {
- imap->entries[lr.entry_index].value = value;
- return;
- }
-
- bh__imap_entry entry;
- entry.key = key;
- entry.value = value;
- entry.next = imap->hashes[lr.hash_index];
- bh_arr_push(imap->entries, entry);
-
- imap->hashes[lr.hash_index] = bh_arr_length(imap->entries) - 1;
-}
-
-b32 bh_imap_has(bh_imap* imap, bh_imap_entry_t key) {
- bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
- return lr.entry_index >= 0;
-}
-
-bh_imap_entry_t bh_imap_get(bh_imap* imap, bh_imap_entry_t key) {
- bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
- if (lr.entry_index >= 0) {
- return imap->entries[lr.entry_index].value;
- } else {
- return 0;
- }
-}
-
-void bh_imap_delete(bh_imap* imap, bh_imap_entry_t key) {
- bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
- if (lr.entry_index < 0) return;
-
- if (lr.entry_prev < 0) {
- imap->hashes[lr.hash_index] = imap->entries[lr.entry_index].next;
- } else {
- imap->entries[lr.entry_prev].next = imap->entries[lr.entry_index].next;
- }
-
- // If it's that last thing in the array, just pop off the end
- if (lr.entry_index == bh_arr_length(imap->entries) - 1) {
- bh_arr_pop(imap->entries);
- return;
- }
-
- bh_arr_fastdelete(imap->entries, lr.entry_index);
- bh__imap_lookup_result last = bh__imap_lookup(imap, imap->entries[lr.entry_index].key);
- if (last.entry_prev >= 0) {
- imap->entries[last.entry_prev].next = lr.entry_index;
- } else {
- imap->hashes[last.hash_index] = lr.entry_index;
- }
-}
-
-void bh_imap_clear(bh_imap* imap) {
- // NOTE: Does not clear out an of the data that was in the map
- bh_arr_each(i64, hash, imap->hashes) *hash = -1;
- bh_arr_set_length(imap->entries, 0);
-}
-
-#endif // ifndef BH_NO_IMAP
-
-
-
-
-
-u64 bh_time_curr() {
-#if defined(_BH_WINDOWS)
- LARGE_INTEGER result;
- QueryPerformanceCounter(&result);
- return (u64) result.QuadPart;
-
-#elif defined(_BH_LINUX)
- struct timespec spec;
- clock_gettime(CLOCK_REALTIME, &spec);
-
- time_t sec = spec.tv_sec;
- u64 ms = spec.tv_nsec / 1000000;
- if (ms > 999) {
- sec++;
- ms = 0;
- }
-
- return sec * 1000 + ms;
-#endif
-}
-
-u64 bh_time_duration(u64 old) {
-#if defined(_BH_WINDOWS)
- u64 curr = bh_time_curr();
- u64 duration = curr - old;
-
- LARGE_INTEGER freq;
- QueryPerformanceFrequency(&freq);
- duration *= 1000;
- duration /= freq.QuadPart;
- return duration;
-
-#elif defined(_BH_LINUX)
- u64 curr = bh_time_curr();
- return curr - old;
-#endif
-}
-
-#endif // ifdef BH_DEFINE
-
-#endif // ifndef BH_H
+++ /dev/null
-#ifndef ONYXDOC_H
-#define ONYXDOC_H
-
-#include "bh.h"
-#include "astnodes.h"
-
-typedef enum DocFormat {
- Doc_Format_Human,
- Doc_Format_Tags,
- Doc_Format_Html,
-} DocFormat;
-
-typedef struct DocEntry {
- OnyxFilePos pos;
- char* sym; // Unused by doc generator
- char* def;
- char* additional;
-} DocEntry;
-
-typedef struct DocPackage {
- const char* name;
-
- bh_arr(DocEntry) public_entries;
- bh_arr(DocEntry) private_entries;
-} DocPackage;
-
-typedef struct OnyxDocumentation {
- bh_arena doc_arena;
-
- DocFormat format;
-
- bh_arr(DocPackage) package_docs;
-} OnyxDocumentation;
-
-OnyxDocumentation onyx_docs_generate();
-void onyx_docs_emit(OnyxDocumentation* doc, const char* filename);
-
-#endif
+++ /dev/null
-#ifndef ONYXERRORS_H
-#define ONYXERRORS_H
-
-#include "bh.h"
-#include "lex.h"
-
-#include <stdarg.h>
-
-typedef enum OnyxErrorRank {
- Error_Undefined = 0,
- Error_Other = 1,
- Error_Warning = 2,
- Error_Waiting_On = 3,
- Error_Critical = 4,
-} OnyxErrorRank;
-
-typedef struct OnyxError {
- OnyxFilePos pos;
- OnyxErrorRank rank;
- 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_submit_error(OnyxError error);
-void onyx_report_error(OnyxFilePos pos, OnyxErrorRank rank, char * format, ...);
-void onyx_submit_warning(OnyxError error);
-void onyx_report_warning(OnyxFilePos pos, char* format, ...);
-void onyx_errors_print();
-b32 onyx_has_errors();
-void onyx_clear_errors();
-
-#endif
+++ /dev/null
-#ifndef ONYXLEX_H
-#define ONYXLEX_H
-
-#include "bh.h"
-
-// NOTE: Used for global statistics
-extern u64 lexer_lines_processed;
-extern u64 lexer_tokens_processed;
-
-typedef enum TokenType {
- Token_Type_Ascii_End = 256,
- Token_Type_Unknown = 256,
- Token_Type_End_Stream = 257,
-
- Token_Type_Comment = 258,
-
- Token_Type_Keyword_Package,
- Token_Type_Keyword_Struct,
- Token_Type_Keyword_Enum,
- Token_Type_Keyword_Use,
- Token_Type_Keyword_If,
- Token_Type_Keyword_Else,
- Token_Type_Keyword_Elseif,
- Token_Type_Keyword_Return,
- Token_Type_Keyword_Global,
- Token_Type_Keyword_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_Keyword_Interface,
- Token_Type_Keyword_Where,
-
- 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);
-b32 token_same_file(OnyxToken *tkn1, OnyxToken *tkn2);
-
-#endif
+++ /dev/null
-
-#include "wasm.h"
-
-#if defined(_WIN32) || defined(_WIN64)
- #define ONYX_EXPORT extern __declspec(dllexport)
- #define ONYX_IMPORT extern __declspec(dllimport)
-#endif
-
-#if defined(__unix__)
- #define ONYX_EXPORT
- #define ONYX_IMPORT
-#endif
-
-typedef struct OnyxRuntime {
- wasm_instance_t* wasm_instance;
- wasm_module_t* wasm_module;
- wasm_memory_t* wasm_memory;
- wasm_engine_t *wasm_engine;
- wasm_extern_vec_t wasm_imports;
-
- int argc;
- char **argv;
-
- // HACK HACK HACK
- // There should need to be this much stuff in here, but because Wasmer doesn't ship a "wasmerdll.lib"
- // file for windows, it is impossible for it to link successfully against the function provided in onyx.exe.
- // Therefore, we must serve as the linker and do this manually. Hopefully that library file will be
- // shipped soon so this can go away...
- char* (*wasm_memory_data)(wasm_memory_t *wasm_memory);
- wasm_extern_t* (*wasm_extern_lookup_by_name)(wasm_module_t* module, wasm_instance_t* instance, const char* name);
- wasm_func_t* (*wasm_extern_as_func)(wasm_extern_t* ext);
- wasm_trap_t* (*wasm_func_call)(const wasm_func_t* wasm_func, const wasm_val_vec_t* args, wasm_val_vec_t* results);
- wasm_instance_t* (*wasm_instance_new)(wasm_store_t* store, const wasm_module_t* module, const wasm_extern_vec_t* imports, wasm_trap_t** traps);
- void (*onyx_print_trap)(wasm_trap_t* trap);
- wasm_store_t *(*wasm_store_new)(wasm_engine_t *engine);
- void (*wasm_store_delete)(wasm_store_t *store);
-} OnyxRuntime;
-
-OnyxRuntime* runtime;
-
-typedef struct WasmValkindBuffer {
- unsigned int count;
- wasm_valkind_t types[20];
-} WasmValkindBuffer;
-
-typedef struct WasmFuncDefinition {
- char* module_name;
- char* import_name;
- wasm_func_callback_t func;
-
- WasmValkindBuffer *params;
- WasmValkindBuffer *results;
-} WasmFuncDefinition;
-
-#define STRINGIFY1(a) #a
-#define STRINGIFY2(a) STRINGIFY1(a)
-#define CONCAT2(a, b) a ## _ ## b
-#define CONCAT3(a, b, c) a ## _ ## b ## _ ## c
-#define ONYX_MODULE_NAME_GEN(m) CONCAT2(__onyx_library, m)
-#define ONYX_LINK_NAME_GEN(m) CONCAT2(onyx_library, m)
-#define ONYX_FUNC_NAME(m, n) CONCAT3(__onyx_internal, m, n)
-#define ONYX_DEF_NAME(m, n) CONCAT3(__onyx_internal_def, m, n)
-#define ONYX_PARAM_NAME(m, n) CONCAT3(__onyx_internal_param_buffer, m, n)
-#define ONYX_RESULT_NAME(m, n) CONCAT3(__onyx_internal_result_buffer, m, n)
-#define ONYX_IMPORT_NAME(m, n) STRINGIFY1(m) "_" #n
-
-#define NUM_VALS(...) (sizeof((wasm_valkind_t []){ 0, __VA_ARGS__ }) / sizeof(wasm_valkind_t))
-#define _VALS(...) { NUM_VALS(__VA_ARGS__) - 1, __VA_ARGS__ }
-
-#define ONYX_DEF(name, params_types, result_types) \
- static wasm_trap_t* ONYX_FUNC_NAME(ONYX_LIBRARY_NAME, name)(const wasm_val_vec_t* params, wasm_val_vec_t* results); \
- static struct WasmValkindBuffer ONYX_PARAM_NAME(ONYX_LIBRARY_NAME, name) = _VALS params_types; \
- static struct WasmValkindBuffer ONYX_RESULT_NAME(ONYX_LIBRARY_NAME, name) = _VALS result_types; \
- static struct WasmFuncDefinition ONYX_DEF_NAME(ONYX_LIBRARY_NAME, name) = { STRINGIFY2(ONYX_LIBRARY_NAME), #name, ONYX_FUNC_NAME(ONYX_LIBRARY_NAME, name), & ONYX_PARAM_NAME(ONYX_LIBRARY_NAME, name), & ONYX_RESULT_NAME(ONYX_LIBRARY_NAME, name) }; \
- \
- static wasm_trap_t* ONYX_FUNC_NAME(ONYX_LIBRARY_NAME, name)(const wasm_val_vec_t* params, wasm_val_vec_t* results)
-
-#define ONYX_FUNC(name) & ONYX_DEF_NAME(ONYX_LIBRARY_NAME, name),
-#define ONYX_LIBRARY \
- extern struct WasmFuncDefinition *ONYX_MODULE_NAME_GEN(ONYX_LIBRARY_NAME)[]; \
- ONYX_EXPORT WasmFuncDefinition** ONYX_LINK_NAME_GEN(ONYX_LIBRARY_NAME)(OnyxRuntime* in_runtime) { \
- runtime = in_runtime; \
- return ONYX_MODULE_NAME_GEN(ONYX_LIBRARY_NAME); \
- } \
- struct WasmFuncDefinition *ONYX_MODULE_NAME_GEN(ONYX_LIBRARY_NAME)[] =
-
-// Shorter names
-#ifndef ONYX_NO_SHORT_NAMES
-#undef BOOL
-#undef INT
-#undef LONG
-#undef FLOAT
-#undef DOUBLE
-#define BOOL WASM_I32
-#define INT WASM_I32
-#define LONG WASM_I64
-#define FLOAT WASM_F32
-#define DOUBLE WASM_F64
-#define PTR WASM_I32
-#endif
-
-#define ONYX_PTR(p) ((void*) (p != 0 ? (runtime->wasm_memory_data(runtime->wasm_memory) + p) : NULL))
+++ /dev/null
-#ifndef ONYXPARSER_H
-#define ONYXPARSER_H
-
-#include "bh.h"
-
-#include "lex.h"
-#include "errors.h"
-#include "astnodes.h"
-
-typedef struct PolymorphicContext {
- AstType* root_node;
- bh_arr(AstPolyParam)* poly_params;
-} PolymorphicContext;
-
-typedef struct OnyxParser {
- bh_allocator allocator;
-
- Package *package;
- Scope *file_scope;
-
- // NOTE: not used since all tokens are lexed before parsing starts
- OnyxTokenizer *tokenizer;
- OnyxToken *prev;
- OnyxToken *curr;
-
- PolymorphicContext polymorph_context;
-
- bh_arr(AstFunction *) current_function_stack;
- Scope *current_scope;
- bh_arr(bh_arr(Entity *) *) alternate_entity_placement_stack;
- bh_arr(OnyxToken *) current_symbol_stack;
-
- bh_arr(AstFlags) scope_flags;
-
- bh_arr(AstTyped *) stored_tags;
-
- u16 tag_depth : 16;
-
- b32 hit_unexpected_token : 1;
- b32 parse_calls : 1;
-} OnyxParser;
-
-const char* onyx_ast_node_kind_string(AstKind kind);
-void* onyx_ast_node_new(bh_allocator alloc, i32 size, AstKind kind);
-OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer);
-void onyx_parser_free(OnyxParser* parser);
-void onyx_parse(OnyxParser *parser);
-
-#endif // #ifndef ONYXPARSER_H
+++ /dev/null
-// Bill's Mini Windows.h from https://github.com/odin-lang/Odin/blob/master/src/gb/gb.h
-
-////////////////////////////////////////////////////////////////
-//
-// Bill's Mini Windows.h
-//
-//
-
-#define GB_EXTERN extern
-#define GB_DLL_EXPORT GB_EXTERN __declspec(dllexport)
-#define GB_DLL_IMPORT GB_EXTERN __declspec(dllimport)
-
-#define WINAPI __stdcall
-#define WINAPIV __cdecl
-#define CALLBACK __stdcall
-#define MAX_PATH 260
-#define CCHDEVICENAME 32
-#define CCHFORMNAME 32
-
-typedef unsigned long DWORD;
-typedef int WINBOOL;
-#ifndef XFree86Server
- #ifndef __OBJC__
- typedef WINBOOL BOOL;
- #else
- #define BOOL WINBOOL
- #endif
-typedef unsigned char BYTE;
-#endif
-typedef unsigned short WORD;
-typedef float FLOAT;
-typedef int INT;
-typedef unsigned int UINT;
-typedef short SHORT;
-typedef long LONG;
-typedef long long LONGLONG;
-typedef unsigned short USHORT;
-typedef unsigned long ULONG;
-typedef unsigned long long ULONGLONG;
-
-typedef UINT WPARAM;
-typedef LONG LPARAM;
-typedef LONG LRESULT;
-#ifndef _HRESULT_DEFINED
-typedef LONG HRESULT;
-#define _HRESULT_DEFINED
-#endif
-#ifndef XFree86Server
-typedef WORD ATOM;
-#endif /* XFree86Server */
-typedef void *HANDLE;
-typedef HANDLE HGLOBAL;
-typedef HANDLE HLOCAL;
-typedef HANDLE GLOBALHANDLE;
-typedef HANDLE LOCALHANDLE;
-typedef void *HGDIOBJ;
-
-#define DECLARE_HANDLE(name) typedef HANDLE name
-DECLARE_HANDLE(HACCEL);
-DECLARE_HANDLE(HBITMAP);
-DECLARE_HANDLE(HBRUSH);
-DECLARE_HANDLE(HCOLORSPACE);
-DECLARE_HANDLE(HDC);
-DECLARE_HANDLE(HGLRC);
-DECLARE_HANDLE(HDESK);
-DECLARE_HANDLE(HENHMETAFILE);
-DECLARE_HANDLE(HFONT);
-DECLARE_HANDLE(HICON);
-DECLARE_HANDLE(HKEY);
-typedef HKEY *PHKEY;
-DECLARE_HANDLE(HMENU);
-DECLARE_HANDLE(HMETAFILE);
-DECLARE_HANDLE(HINSTANCE);
-typedef HINSTANCE HMODULE;
-DECLARE_HANDLE(HPALETTE);
-DECLARE_HANDLE(HPEN);
-DECLARE_HANDLE(HRGN);
-DECLARE_HANDLE(HRSRC);
-DECLARE_HANDLE(HSTR);
-DECLARE_HANDLE(HTASK);
-DECLARE_HANDLE(HWND);
-DECLARE_HANDLE(HWINSTA);
-DECLARE_HANDLE(HKL);
-DECLARE_HANDLE(HRAWINPUT);
-DECLARE_HANDLE(HMONITOR);
-#undef DECLARE_HANDLE
-
-typedef int HFILE;
-typedef HICON HCURSOR;
-typedef DWORD COLORREF;
-typedef int (WINAPI *FARPROC)();
-typedef int (WINAPI *NEARPROC)();
-typedef int (WINAPI *PROC)();
-typedef LRESULT (CALLBACK *WNDPROC)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
-
-#if defined(_WIN64)
-typedef unsigned __int64 ULONG_PTR;
-typedef signed __int64 LONG_PTR;
-#else
-typedef unsigned long ULONG_PTR;
-typedef signed long LONG_PTR;
-#endif
-typedef ULONG_PTR DWORD_PTR;
-
-typedef struct tagRECT {
- LONG left;
- LONG top;
- LONG right;
- LONG bottom;
-} RECT;
-typedef struct tagRECTL {
- LONG left;
- LONG top;
- LONG right;
- LONG bottom;
-} RECTL;
-typedef struct tagPOINT {
- LONG x;
- LONG y;
-} POINT;
-typedef struct tagSIZE {
- LONG cx;
- LONG cy;
-} SIZE;
-typedef struct tagPOINTS {
- SHORT x;
- SHORT y;
-} POINTS;
-typedef struct _SECURITY_ATTRIBUTES {
- DWORD nLength;
- HANDLE lpSecurityDescriptor;
- BOOL bInheritHandle;
-} SECURITY_ATTRIBUTES;
-typedef enum _LOGICAL_PROCESSOR_RELATIONSHIP {
- RelationProcessorCore,
- RelationNumaNode,
- RelationCache,
- RelationProcessorPackage,
- RelationGroup,
- RelationAll = 0xffff
-} LOGICAL_PROCESSOR_RELATIONSHIP;
-typedef enum _PROCESSOR_CACHE_TYPE {
- CacheUnified,
- CacheInstruction,
- CacheData,
- CacheTrace
-} PROCESSOR_CACHE_TYPE;
-typedef struct _CACHE_DESCRIPTOR {
- BYTE Level;
- BYTE Associativity;
- WORD LineSize;
- DWORD Size;
- PROCESSOR_CACHE_TYPE Type;
-} CACHE_DESCRIPTOR;
-typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION {
- ULONG_PTR ProcessorMask;
- LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
- union {
- struct {
- BYTE Flags;
- } ProcessorCore;
- struct {
- DWORD NodeNumber;
- } NumaNode;
- CACHE_DESCRIPTOR Cache;
- ULONGLONG Reserved[2];
- };
-} SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
-typedef struct _MEMORY_BASIC_INFORMATION {
- void *BaseAddress;
- void *AllocationBase;
- DWORD AllocationProtect;
- size_t RegionSize;
- DWORD State;
- DWORD Protect;
- DWORD Type;
-} MEMORY_BASIC_INFORMATION;
-typedef struct _SYSTEM_INFO {
- union {
- DWORD dwOemId;
- struct {
- WORD wProcessorArchitecture;
- WORD wReserved;
- };
- };
- DWORD dwPageSize;
- void * lpMinimumApplicationAddress;
- void * lpMaximumApplicationAddress;
- DWORD_PTR dwActiveProcessorMask;
- DWORD dwNumberOfProcessors;
- DWORD dwProcessorType;
- DWORD dwAllocationGranularity;
- WORD wProcessorLevel;
- WORD wProcessorRevision;
-} SYSTEM_INFO;
-typedef union _LARGE_INTEGER {
- struct {
- DWORD LowPart;
- LONG HighPart;
- };
- struct {
- DWORD LowPart;
- LONG HighPart;
- } u;
- LONGLONG QuadPart;
-} LARGE_INTEGER;
-typedef union _ULARGE_INTEGER {
- struct {
- DWORD LowPart;
- DWORD HighPart;
- };
- struct {
- DWORD LowPart;
- DWORD HighPart;
- } u;
- ULONGLONG QuadPart;
-} ULARGE_INTEGER;
-
-typedef struct _OVERLAPPED {
- ULONG_PTR Internal;
- ULONG_PTR InternalHigh;
- union {
- struct {
- DWORD Offset;
- DWORD OffsetHigh;
- };
- void *Pointer;
- };
- HANDLE hEvent;
-} OVERLAPPED;
-typedef struct _FILETIME {
- DWORD dwLowDateTime;
- DWORD dwHighDateTime;
-} FILETIME;
-typedef struct _WIN32_FIND_DATAW {
- DWORD dwFileAttributes;
- FILETIME ftCreationTime;
- FILETIME ftLastAccessTime;
- FILETIME ftLastWriteTime;
- DWORD nFileSizeHigh;
- DWORD nFileSizeLow;
- DWORD dwReserved0;
- DWORD dwReserved1;
- wchar_t cFileName[MAX_PATH];
- wchar_t cAlternateFileName[14];
-} WIN32_FIND_DATAW;
-typedef struct _WIN32_FIND_DATAA {
- DWORD dwFileAttributes;
- FILETIME ftCreationTime;
- FILETIME ftLastAccessTime;
- FILETIME ftLastWriteTime;
- DWORD nFileSizeHigh;
- DWORD nFileSizeLow;
- DWORD dwReserved0;
- DWORD dwReserved1;
- char cFileName[MAX_PATH];
- char cAlternateFileName[14];
-} WIN32_FIND_DATAA;
-typedef struct _WIN32_FILE_ATTRIBUTE_DATA {
- DWORD dwFileAttributes;
- FILETIME ftCreationTime;
- FILETIME ftLastAccessTime;
- FILETIME ftLastWriteTime;
- DWORD nFileSizeHigh;
- DWORD nFileSizeLow;
-} WIN32_FILE_ATTRIBUTE_DATA;
-typedef enum _GET_FILEEX_INFO_LEVELS {
- GetFileExInfoStandard,
- GetFileExMaxInfoLevel
-} GET_FILEEX_INFO_LEVELS;
-typedef struct _STARTUPINFOA {
- DWORD cb;
- char * lpReserved;
- char * lpDesktop;
- char * lpTitle;
- DWORD dwX;
- DWORD dwY;
- DWORD dwXSize;
- DWORD dwYSize;
- DWORD dwXCountChars;
- DWORD dwYCountChars;
- DWORD dwFillAttribute;
- DWORD dwFlags;
- WORD wShowWindow;
- WORD cbReserved2;
- char * lpReserved2;
- HANDLE hStdInput;
- HANDLE hStdOutput;
- HANDLE hStdError;
-} STARTUPINFOA, *LPSTARTUPINFOA;
-typedef struct _PROCESS_INFORMATION {
- HANDLE hProcess;
- HANDLE hThread;
- DWORD dwProcessId;
- DWORD dwThreadId;
-} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
-
-#define INFINITE 0xffffffffl
-#define INVALID_HANDLE_VALUE ((void *)(intptr_t)(-1))
-#define STARTF_USESTDHANDLES 0x00000100
-
-typedef DWORD WINAPI THREAD_START_ROUTINE(void *parameter);
-
-GB_DLL_IMPORT DWORD WINAPI GetLastError (void);
-GB_DLL_IMPORT BOOL WINAPI CloseHandle (HANDLE object);
-GB_DLL_IMPORT HANDLE WINAPI CreateSemaphoreA (SECURITY_ATTRIBUTES *semaphore_attributes, LONG initial_count,
- LONG maximum_count, char const *name);
-GB_DLL_IMPORT BOOL WINAPI ReleaseSemaphore (HANDLE semaphore, LONG release_count, LONG *previous_count);
-GB_DLL_IMPORT DWORD WINAPI WaitForSingleObject(HANDLE handle, DWORD milliseconds);
-GB_DLL_IMPORT HANDLE WINAPI CreateThread (SECURITY_ATTRIBUTES *semaphore_attributes, size_t stack_size,
- THREAD_START_ROUTINE *start_address, void *parameter,
- DWORD creation_flags, DWORD *thread_id);
-GB_DLL_IMPORT DWORD WINAPI GetThreadId (HANDLE handle);
-GB_DLL_IMPORT void WINAPI RaiseException (DWORD, DWORD, DWORD, ULONG_PTR const *);
-GB_DLL_IMPORT BOOL WINAPI TerminateThread (HANDLE hThread, DWORD dwExitCode);
-GB_DLL_IMPORT BOOL WINAPI CreateProcessA (char const * lpApplicationName, char * lpCommandLine,
- SECURITY_ATTRIBUTES* lpProcessAttrs, SECURITY_ATTRIBUTES* lpThreadAttributes,
- BOOL bInheritHandles, DWORD dwCreationFlags, void* lpEnvironment,
- char const * lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo,
- LPPROCESS_INFORMATION lpProcessInformation);
-GB_DLL_IMPORT BOOL WINAPI GetExitCodeProcess (HANDLE hProcess, DWORD *lpExitCode);
-GB_DLL_IMPORT BOOL WINAPI CreatePipe (HANDLE *hReadPipe, HANDLE *hWritePipe, SECURITY_ATTRIBUTES* lpPipeAttributes,
- DWORD nSize);
-GB_DLL_IMPORT BOOL WINAPI TerminateProcess (HANDLE hProcess, UINT uExitCode);
-GB_DLL_IMPORT BOOL WINAPI SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags);
-
-uintptr_t _beginthreadex( // NATIVE CODE
- void *security,
- unsigned stack_size,
- unsigned ( __stdcall *start_address )( void * ),
- void *arglist,
- unsigned initflag,
- unsigned *thrdaddr
-);
-
-
-GB_DLL_IMPORT BOOL WINAPI GetLogicalProcessorInformation(SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buffer, DWORD *return_length);
-GB_DLL_IMPORT DWORD_PTR WINAPI SetThreadAffinityMask(HANDLE thread, DWORD_PTR check_mask);
-GB_DLL_IMPORT HANDLE WINAPI GetCurrentThread(void);
-
-#define PAGE_NOACCESS 0x01
-#define PAGE_READONLY 0x02
-#define PAGE_READWRITE 0x04
-#define PAGE_WRITECOPY 0x08
-#define PAGE_EXECUTE 0x10
-#define PAGE_EXECUTE_READ 0x20
-#define PAGE_EXECUTE_READWRITE 0x40
-#define PAGE_EXECUTE_WRITECOPY 0x80
-#define PAGE_GUARD 0x100
-#define PAGE_NOCACHE 0x200
-#define PAGE_WRITECOMBINE 0x400
-
-#define MEM_COMMIT 0x1000
-#define MEM_RESERVE 0x2000
-#define MEM_DECOMMIT 0x4000
-#define MEM_RELEASE 0x8000
-#define MEM_FREE 0x10000
-#define MEM_PRIVATE 0x20000
-#define MEM_MAPPED 0x40000
-#define MEM_RESET 0x80000
-#define MEM_TOP_DOWN 0x100000
-#define MEM_LARGE_PAGES 0x20000000
-#define MEM_4MB_PAGES 0x80000000
-
-
-
-
-GB_DLL_IMPORT void * WINAPI VirtualAlloc (void *addr, size_t size, DWORD allocation_type, DWORD protect);
-GB_DLL_IMPORT size_t WINAPI VirtualQuery (void const *address, MEMORY_BASIC_INFORMATION *buffer, size_t length);
-GB_DLL_IMPORT BOOL WINAPI VirtualFree (void *address, size_t size, DWORD free_type);
-GB_DLL_IMPORT void WINAPI GetSystemInfo(SYSTEM_INFO *system_info);
-
-
-#define GENERIC_READ 0x80000000
-#define GENERIC_WRITE 0x40000000
-#define GENERIC_EXECUTE 0x20000000
-#define GENERIC_ALL 0x10000000
-#define FILE_SHARE_READ 0x00000001
-#define FILE_SHARE_WRITE 0x00000002
-#define FILE_SHARE_DELETE 0x00000004
-#define CREATE_NEW 1
-#define CREATE_ALWAYS 2
-#define OPEN_EXISTING 3
-#define OPEN_ALWAYS 4
-#define TRUNCATE_EXISTING 5
-#define FILE_ATTRIBUTE_READONLY 0x00000001
-#define FILE_ATTRIBUTE_NORMAL 0x00000080
-#define FILE_ATTRIBUTE_TEMPORARY 0x00000100
-#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
-#define ERROR_FILE_NOT_FOUND 2l
-#define ERROR_ACCESS_DENIED 5L
-#define ERROR_NO_MORE_FILES 18l
-#define ERROR_FILE_EXISTS 80l
-#define ERROR_ALREADY_EXISTS 183l
-#define STD_INPUT_HANDLE ((DWORD)-10)
-#define STD_OUTPUT_HANDLE ((DWORD)-11)
-#define STD_ERROR_HANDLE ((DWORD)-12)
-
-GB_DLL_IMPORT int MultiByteToWideChar(UINT code_page, DWORD flags, char const * multi_byte_str, int multi_byte_len, wchar_t const *wide_char_str, int wide_char_len);
-GB_DLL_IMPORT int WideCharToMultiByte(UINT code_page, DWORD flags, wchar_t const *wide_char_str, int wide_char_len, char const * multi_byte_str, int multi_byte_len);
-GB_DLL_IMPORT BOOL WINAPI SetFilePointerEx(HANDLE file, LARGE_INTEGER distance_to_move,
- LARGE_INTEGER *new_file_pointer, DWORD move_method);
-GB_DLL_IMPORT BOOL WINAPI ReadFile (HANDLE file, void *buffer, DWORD bytes_to_read, DWORD *bytes_read, OVERLAPPED *overlapped);
-GB_DLL_IMPORT BOOL WINAPI WriteFile (HANDLE file, void const *buffer, DWORD bytes_to_write, DWORD *bytes_written, OVERLAPPED *overlapped);
-GB_DLL_IMPORT HANDLE WINAPI CreateFileW (wchar_t const *path, DWORD desired_access, DWORD share_mode,
- SECURITY_ATTRIBUTES *, DWORD creation_disposition,
- DWORD flags_and_attributes, HANDLE template_file);
-GB_DLL_IMPORT HANDLE WINAPI CreateFileA (char const *path, DWORD desired_access, DWORD share_mode,
- SECURITY_ATTRIBUTES *, DWORD creation_disposition,
- DWORD flags_and_attributes, HANDLE template_file);
-GB_DLL_IMPORT HANDLE WINAPI GetStdHandle (DWORD std_handle);
-GB_DLL_IMPORT BOOL WINAPI GetFileSizeEx (HANDLE file, LARGE_INTEGER *size);
-GB_DLL_IMPORT BOOL WINAPI SetEndOfFile (HANDLE file);
-GB_DLL_IMPORT HANDLE WINAPI FindFirstFileW (wchar_t const *path, WIN32_FIND_DATAW *data);
-GB_DLL_IMPORT HANDLE WINAPI FindFirstFileA (char const *path, WIN32_FIND_DATAA *data);
-GB_DLL_IMPORT BOOL WINAPI FindNextFileA (HANDLE find_find, WIN32_FIND_DATAA *data);
-GB_DLL_IMPORT BOOL WINAPI FindClose (HANDLE find_file);
-GB_DLL_IMPORT BOOL WINAPI GetFileAttributesExW(wchar_t const *path, GET_FILEEX_INFO_LEVELS info_level_id, WIN32_FILE_ATTRIBUTE_DATA *data);
-GB_DLL_IMPORT BOOL WINAPI CopyFileW(wchar_t const *old_f, wchar_t const *new_f, BOOL fail_if_exists);
-GB_DLL_IMPORT BOOL WINAPI MoveFileW(wchar_t const *old_f, wchar_t const *new_f);
-GB_DLL_IMPORT BOOL WINAPI DeleteFileA (char const *path);
-GB_DLL_IMPORT BOOL WINAPI CreateDirectoryA(char const *path);
-GB_DLL_IMPORT BOOL WINAPI RemoveDirectoryA(char const *path);
-GB_DLL_IMPORT BOOL WINAPI MoveFileA (char const *old_path, char const *new_path);
-
-GB_DLL_IMPORT DWORD WINAPI GetFullPathNameA(char const *lpFileName, DWORD nBufferLength, char *lpBuffer, char **lpFilePart);
-
-GB_DLL_IMPORT HMODULE WINAPI LoadLibraryA (char const *filename);
-GB_DLL_IMPORT BOOL WINAPI FreeLibrary (HMODULE module);
-GB_DLL_IMPORT FARPROC WINAPI GetProcAddress(HMODULE module, char const *name);
-
-GB_DLL_IMPORT BOOL WINAPI QueryPerformanceFrequency(LARGE_INTEGER *frequency);
-GB_DLL_IMPORT BOOL WINAPI QueryPerformanceCounter (LARGE_INTEGER *counter);
-GB_DLL_IMPORT void WINAPI GetSystemTimeAsFileTime (FILETIME *system_time_as_file_time);
-GB_DLL_IMPORT void WINAPI Sleep(DWORD milliseconds);
-GB_DLL_IMPORT void WINAPI ExitProcess(UINT exit_code);
-
-GB_DLL_IMPORT BOOL WINAPI SetEnvironmentVariableA(char const *name, char const *value);
-GB_DLL_IMPORT DWORD WINAPI GetEnvironmentVariableA(char const * lpName, char * lpBuffer, DWORD nSize);
-
-GB_DLL_IMPORT short WINAPI htons(short hostshort);
-GB_DLL_IMPORT int WINAPI htonl(int hostint);
-GB_DLL_IMPORT short WINAPI ntohs(short netshort);
-GB_DLL_IMPORT int WINAPI ntohl(int netint);
+++ /dev/null
-/* stb_ds.h - v0.67 - public domain data structures - Sean Barrett 2019
-
- This is a single-header-file library that provides easy-to-use
- dynamic arrays and hash tables for C (also works in C++).
-
- For a gentle introduction:
- http://nothings.org/stb_ds
-
- To use this library, do this in *one* C or C++ file:
- #define STB_DS_IMPLEMENTATION
- #include "stb_ds.h"
-
-TABLE OF CONTENTS
-
- Table of Contents
- Compile-time options
- License
- Documentation
- Notes
- Notes - Dynamic arrays
- Notes - Hash maps
- Credits
-
-COMPILE-TIME OPTIONS
-
- #define STBDS_NO_SHORT_NAMES
-
- This flag needs to be set globally.
-
- By default stb_ds exposes shorter function names that are not qualified
- with the "stbds_" prefix. If these names conflict with the names in your
- code, define this flag.
-
- #define STBDS_SIPHASH_2_4
-
- This flag only needs to be set in the file containing #define STB_DS_IMPLEMENTATION.
-
- By default stb_ds.h hashes using a weaker variant of SipHash and a custom hash for
- 4- and 8-byte keys. On 64-bit platforms, you can define the above flag to force
- stb_ds.h to use specification-compliant SipHash-2-4 for all keys. Doing so makes
- hash table insertion about 20% slower on 4- and 8-byte keys, 5% slower on
- 64-byte keys, and 10% slower on 256-byte keys on my test computer.
-
- #define STBDS_REALLOC(context,ptr,size) better_realloc
- #define STBDS_FREE(context,ptr) better_free
-
- These defines only need to be set in the file containing #define STB_DS_IMPLEMENTATION.
-
- By default stb_ds uses stdlib realloc() and free() for memory management. You can
- substitute your own functions instead by defining these symbols. You must either
- define both, or neither. Note that at the moment, 'context' will always be NULL.
- @TODO add an array/hash initialization function that takes a memory context pointer.
-
- #define STBDS_UNIT_TESTS
-
- Defines a function stbds_unit_tests() that checks the functioning of the data structures.
-
- Note that on older versions of gcc (e.g. 5.x.x) you may need to build with '-std=c++0x'
- (or equivalentally '-std=c++11') when using anonymous structures as seen on the web
- page or in STBDS_UNIT_TESTS.
-
-LICENSE
-
- Placed in the public domain and also MIT licensed.
- See end of file for detailed license information.
-
-DOCUMENTATION
-
- Dynamic Arrays
-
- Non-function interface:
-
- Declare an empty dynamic array of type T
- T* foo = NULL;
-
- Access the i'th item of a dynamic array 'foo' of type T, T* foo:
- foo[i]
-
- Functions (actually macros)
-
- arrfree:
- void arrfree(T*);
- Frees the array.
-
- arrlen:
- ptrdiff_t arrlen(T*);
- Returns the number of elements in the array.
-
- arrlenu:
- size_t arrlenu(T*);
- Returns the number of elements in the array as an unsigned type.
-
- arrpop:
- T arrpop(T* a)
- Removes the final element of the array and returns it.
-
- arrput:
- T arrput(T* a, T b);
- Appends the item b to the end of array a. Returns b.
-
- arrins:
- T arrins(T* a, int p, T b);
- Inserts the item b into the middle of array a, into a[p],
- moving the rest of the array over. Returns b.
-
- arrinsn:
- void arrinsn(T* a, int p, int n);
- Inserts n uninitialized items into array a starting at a[p],
- moving the rest of the array over.
-
- arraddnptr:
- T* arraddnptr(T* a, int n)
- Appends n uninitialized items onto array at the end.
- Returns a pointer to the first uninitialized item added.
-
- arraddnindex:
- size_t arraddnindex(T* a, int n)
- Appends n uninitialized items onto array at the end.
- Returns the index of the first uninitialized item added.
-
- arrdel:
- void arrdel(T* a, int p);
- Deletes the element at a[p], moving the rest of the array over.
-
- arrdeln:
- void arrdeln(T* a, int p, int n);
- Deletes n elements starting at a[p], moving the rest of the array over.
-
- arrdelswap:
- void arrdelswap(T* a, int p);
- Deletes the element at a[p], replacing it with the element from
- the end of the array. O(1) performance.
-
- arrsetlen:
- void arrsetlen(T* a, int n);
- Changes the length of the array to n. Allocates uninitialized
- slots at the end if necessary.
-
- arrsetcap:
- size_t arrsetcap(T* a, int n);
- Sets the length of allocated storage to at least n. It will not
- change the length of the array.
-
- arrcap:
- size_t arrcap(T* a);
- Returns the number of total elements the array can contain without
- needing to be reallocated.
-
- Hash maps & String hash maps
-
- Given T is a structure type: struct { TK key; TV value; }. Note that some
- functions do not require TV value and can have other fields. For string
- hash maps, TK must be 'char *'.
-
- Special interface:
-
- stbds_rand_seed:
- void stbds_rand_seed(size_t seed);
- For security against adversarially chosen data, you should seed the
- library with a strong random number. Or at least seed it with time().
-
- stbds_hash_string:
- size_t stbds_hash_string(char *str, size_t seed);
- Returns a hash value for a string.
-
- stbds_hash_bytes:
- size_t stbds_hash_bytes(void *p, size_t len, size_t seed);
- These functions hash an arbitrary number of bytes. The function
- uses a custom hash for 4- and 8-byte data, and a weakened version
- of SipHash for everything else. On 64-bit platforms you can get
- specification-compliant SipHash-2-4 on all data by defining
- STBDS_SIPHASH_2_4, at a significant cost in speed.
-
- Non-function interface:
-
- Declare an empty hash map of type T
- T* foo = NULL;
-
- Access the i'th entry in a hash table T* foo:
- foo[i]
-
- Function interface (actually macros):
-
- hmfree
- shfree
- void hmfree(T*);
- void shfree(T*);
- Frees the hashmap and sets the pointer to NULL.
-
- hmlen
- shlen
- ptrdiff_t hmlen(T*)
- ptrdiff_t shlen(T*)
- Returns the number of elements in the hashmap.
-
- hmlenu
- shlenu
- size_t hmlenu(T*)
- size_t shlenu(T*)
- Returns the number of elements in the hashmap.
-
- hmgeti
- shgeti
- hmgeti_ts
- ptrdiff_t hmgeti(T*, TK key)
- ptrdiff_t shgeti(T*, char* key)
- ptrdiff_t hmgeti_ts(T*, TK key, ptrdiff_t tempvar)
- Returns the index in the hashmap which has the key 'key', or -1
- if the key is not present.
-
- hmget
- hmget_ts
- shget
- TV hmget(T*, TK key)
- TV shget(T*, char* key)
- TV hmget_ts(T*, TK key, ptrdiff_t tempvar)
- Returns the value corresponding to 'key' in the hashmap.
- The structure must have a 'value' field
-
- hmgets
- shgets
- T hmgets(T*, TK key)
- T shgets(T*, char* key)
- Returns the structure corresponding to 'key' in the hashmap.
-
- hmgetp
- shgetp
- hmgetp_ts
- hmgetp_null
- shgetp_null
- T* hmgetp(T*, TK key)
- T* shgetp(T*, char* key)
- T* hmgetp_ts(T*, TK key, ptrdiff_t tempvar)
- T* hmgetp_null(T*, TK key)
- T* shgetp_null(T*, char *key)
- Returns a pointer to the structure corresponding to 'key' in
- the hashmap. Functions ending in "_null" return NULL if the key
- is not present in the hashmap; the others return a pointer to a
- structure holding the default value (but not the searched-for key).
-
- hmdefault
- shdefault
- TV hmdefault(T*, TV value)
- TV shdefault(T*, TV value)
- Sets the default value for the hashmap, the value which will be
- returned by hmget/shget if the key is not present.
-
- hmdefaults
- shdefaults
- TV hmdefaults(T*, T item)
- TV shdefaults(T*, T item)
- Sets the default struct for the hashmap, the contents which will be
- returned by hmgets/shgets if the key is not present.
-
- hmput
- shput
- TV hmput(T*, TK key, TV value)
- TV shput(T*, char* key, TV value)
- Inserts a <key,value> pair into the hashmap. If the key is already
- present in the hashmap, updates its value.
-
- hmputs
- shputs
- T hmputs(T*, T item)
- T shputs(T*, T item)
- Inserts a struct with T.key into the hashmap. If the struct is already
- present in the hashmap, updates it.
-
- hmdel
- shdel
- int hmdel(T*, TK key)
- int shdel(T*, char* key)
- If 'key' is in the hashmap, deletes its entry and returns 1.
- Otherwise returns 0.
-
- Function interface (actually macros) for strings only:
-
- sh_new_strdup
- void sh_new_strdup(T*);
- Overwrites the existing pointer with a newly allocated
- string hashmap which will automatically allocate and free
- each string key using realloc/free
-
- sh_new_arena
- void sh_new_arena(T*);
- Overwrites the existing pointer with a newly allocated
- string hashmap which will automatically allocate each string
- key to a string arena. Every string key ever used by this
- hash table remains in the arena until the arena is freed.
- Additionally, any key which is deleted and reinserted will
- be allocated multiple times in the string arena.
-
-NOTES
-
- * These data structures are realloc'd when they grow, and the macro
- "functions" write to the provided pointer. This means: (a) the pointer
- must be an lvalue, and (b) the pointer to the data structure is not
- stable, and you must maintain it the same as you would a realloc'd
- pointer. For example, if you pass a pointer to a dynamic array to a
- function which updates it, the function must return back the new
- pointer to the caller. This is the price of trying to do this in C.
-
- * The following are the only functions that are thread-safe on a single data
- structure, i.e. can be run in multiple threads simultaneously on the same
- data structure
- hmlen shlen
- hmlenu shlenu
- hmget_ts shget_ts
- hmgeti_ts shgeti_ts
- hmgets_ts shgets_ts
-
- * You iterate over the contents of a dynamic array and a hashmap in exactly
- the same way, using arrlen/hmlen/shlen:
-
- for (i=0; i < arrlen(foo); ++i)
- ... foo[i] ...
-
- * All operations except arrins/arrdel are O(1) amortized, but individual
- operations can be slow, so these data structures may not be suitable
- for real time use. Dynamic arrays double in capacity as needed, so
- elements are copied an average of once. Hash tables double/halve
- their size as needed, with appropriate hysteresis to maintain O(1)
- performance.
-
-NOTES - DYNAMIC ARRAY
-
- * If you know how long a dynamic array is going to be in advance, you can avoid
- extra memory allocations by using arrsetlen to allocate it to that length in
- advance and use foo[n] while filling it out, or arrsetcap to allocate the memory
- for that length and use arrput/arrpush as normal.
-
- * Unlike some other versions of the dynamic array, this version should
- be safe to use with strict-aliasing optimizations.
-
-NOTES - HASH MAP
-
- * For compilers other than GCC and clang (e.g. Visual Studio), for hmput/hmget/hmdel
- and variants, the key must be an lvalue (so the macro can take the address of it).
- Extensions are used that eliminate this requirement if you're using C99 and later
- in GCC or clang, or if you're using C++ in GCC. But note that this can make your
- code less portable.
-
- * To test for presence of a key in a hashmap, just do 'hmgeti(foo,key) >= 0'.
-
- * The iteration order of your data in the hashmap is determined solely by the
- order of insertions and deletions. In particular, if you never delete, new
- keys are always added at the end of the array. This will be consistent
- across all platforms and versions of the library. However, you should not
- attempt to serialize the internal hash table, as the hash is not consistent
- between different platforms, and may change with future versions of the library.
-
- * Use sh_new_arena() for string hashmaps that you never delete from. Initialize
- with NULL if you're managing the memory for your strings, or your strings are
- never freed (at least until the hashmap is freed). Otherwise, use sh_new_strdup().
- @TODO: make an arena variant that garbage collects the strings with a trivial
- copy collector into a new arena whenever the table shrinks / rebuilds. Since
- current arena recommendation is to only use arena if it never deletes, then
- this can just replace current arena implementation.
-
- * If adversarial input is a serious concern and you're on a 64-bit platform,
- enable STBDS_SIPHASH_2_4 (see the 'Compile-time options' section), and pass
- a strong random number to stbds_rand_seed.
-
- * The default value for the hash table is stored in foo[-1], so if you
- use code like 'hmget(T,k)->value = 5' you can accidentally overwrite
- the value stored by hmdefault if 'k' is not present.
-
-CREDITS
-
- Sean Barrett -- library, idea for dynamic array API/implementation
- Per Vognsen -- idea for hash table API/implementation
- Rafael Sachetto -- arrpop()
- github:HeroicKatora -- arraddn() reworking
-
- Bugfixes:
- Andy Durdin
- Shane Liesegang
- Vinh Truong
- Andreas Molzer
- github:hashitaku
- github:srdjanstipic
- Macoy Madson
- Andreas Vennstrom
- Tobias Mansfield-Williams
-*/
-
-#ifdef STBDS_UNIT_TESTS
-#define _CRT_SECURE_NO_WARNINGS
-#endif
-
-#ifndef INCLUDE_STB_DS_H
-#define INCLUDE_STB_DS_H
-
-#include <stddef.h>
-#include <string.h>
-
-#ifndef STBDS_NO_SHORT_NAMES
-#define arrlen stbds_arrlen
-#define arrlenu stbds_arrlenu
-#define arrput stbds_arrput
-#define arrpush stbds_arrput
-#define arrpop stbds_arrpop
-#define arrfree stbds_arrfree
-#define arraddn stbds_arraddn // deprecated, use one of the following instead:
-#define arraddnptr stbds_arraddnptr
-#define arraddnindex stbds_arraddnindex
-#define arrsetlen stbds_arrsetlen
-#define arrlast stbds_arrlast
-#define arrins stbds_arrins
-#define arrinsn stbds_arrinsn
-#define arrdel stbds_arrdel
-#define arrdeln stbds_arrdeln
-#define arrdelswap stbds_arrdelswap
-#define arrcap stbds_arrcap
-#define arrsetcap stbds_arrsetcap
-
-#define hmput stbds_hmput
-#define hmputs stbds_hmputs
-#define hmget stbds_hmget
-#define hmget_ts stbds_hmget_ts
-#define hmgets stbds_hmgets
-#define hmgetp stbds_hmgetp
-#define hmgetp_ts stbds_hmgetp_ts
-#define hmgetp_null stbds_hmgetp_null
-#define hmgeti stbds_hmgeti
-#define hmgeti_ts stbds_hmgeti_ts
-#define hmdel stbds_hmdel
-#define hmlen stbds_hmlen
-#define hmlenu stbds_hmlenu
-#define hmfree stbds_hmfree
-#define hmdefault stbds_hmdefault
-#define hmdefaults stbds_hmdefaults
-
-#define shput stbds_shput
-#define shputi stbds_shputi
-#define shputs stbds_shputs
-#define shget stbds_shget
-#define shgeti stbds_shgeti
-#define shgets stbds_shgets
-#define shgetp stbds_shgetp
-#define shgetp_null stbds_shgetp_null
-#define shdel stbds_shdel
-#define shlen stbds_shlen
-#define shlenu stbds_shlenu
-#define shfree stbds_shfree
-#define shdefault stbds_shdefault
-#define shdefaults stbds_shdefaults
-#define sh_new_arena stbds_sh_new_arena
-#define sh_new_strdup stbds_sh_new_strdup
-
-#define stralloc stbds_stralloc
-#define strreset stbds_strreset
-#endif
-
-#if defined(STBDS_REALLOC) && !defined(STBDS_FREE) || !defined(STBDS_REALLOC) && defined(STBDS_FREE)
-#error "You must define both STBDS_REALLOC and STBDS_FREE, or neither."
-#endif
-#if !defined(STBDS_REALLOC) && !defined(STBDS_FREE)
-#include <stdlib.h>
-#define STBDS_REALLOC(c,p,s) realloc(p,s)
-#define STBDS_FREE(c,p) free(p)
-#endif
-
-#ifdef _MSC_VER
-#define STBDS_NOTUSED(v) (void)(v)
-#else
-#define STBDS_NOTUSED(v) (void)sizeof(v)
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// for security against attackers, seed the library with a random number, at least time() but stronger is better
-extern void stbds_rand_seed(size_t seed);
-
-// these are the hash functions used internally if you want to test them or use them for other purposes
-extern size_t stbds_hash_bytes(void *p, size_t len, size_t seed);
-extern size_t stbds_hash_string(char *str, size_t seed);
-
-// this is a simple string arena allocator, initialize with e.g. 'stbds_string_arena my_arena={0}'.
-typedef struct stbds_string_arena stbds_string_arena;
-extern char * stbds_stralloc(stbds_string_arena *a, char *str);
-extern void stbds_strreset(stbds_string_arena *a);
-
-// have to #define STBDS_UNIT_TESTS to call this
-extern void stbds_unit_tests(void);
-
-///////////////
-//
-// Everything below here is implementation details
-//
-
-extern void * stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap);
-extern void stbds_arrfreef(void *a);
-extern void stbds_hmfree_func(void *p, size_t elemsize);
-extern void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode);
-extern void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode);
-extern void * stbds_hmput_default(void *a, size_t elemsize);
-extern void * stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode);
-extern void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode);
-extern void * stbds_shmode_func(size_t elemsize, int mode);
-
-#ifdef __cplusplus
-}
-#endif
-
-#if defined(__GNUC__) || defined(__clang__)
-#define STBDS_HAS_TYPEOF
-#ifdef __cplusplus
-//#define STBDS_HAS_LITERAL_ARRAY // this is currently broken for clang
-#endif
-#endif
-
-#if !defined(__cplusplus)
-#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
-#define STBDS_HAS_LITERAL_ARRAY
-#endif
-#endif
-
-// this macro takes the address of the argument, but on gcc/clang can accept rvalues
-#if defined(STBDS_HAS_LITERAL_ARRAY) && defined(STBDS_HAS_TYPEOF)
- #if __clang__
- #define STBDS_ADDRESSOF(typevar, value) ((__typeof__(typevar)[1]){value}) // literal array decays to pointer to value
- #else
- #define STBDS_ADDRESSOF(typevar, value) ((typeof(typevar)[1]){value}) // literal array decays to pointer to value
- #endif
-#else
-#define STBDS_ADDRESSOF(typevar, value) &(value)
-#endif
-
-#define STBDS_OFFSETOF(var,field) ((char *) &(var)->field - (char *) (var))
-
-#define stbds_header(t) ((stbds_array_header *) (t) - 1)
-#define stbds_temp(t) stbds_header(t)->temp
-#define stbds_temp_key(t) (*(char **) stbds_header(t)->hash_table)
-
-#define stbds_arrsetcap(a,n) (stbds_arrgrow(a,0,n))
-#define stbds_arrsetlen(a,n) ((stbds_arrcap(a) < (size_t) (n) ? stbds_arrsetcap((a),(size_t)(n)),0 : 0), (a) ? stbds_header(a)->length = (size_t) (n) : 0)
-#define stbds_arrcap(a) ((a) ? stbds_header(a)->capacity : 0)
-#define stbds_arrlen(a) ((a) ? (ptrdiff_t) stbds_header(a)->length : 0)
-#define stbds_arrlenu(a) ((a) ? stbds_header(a)->length : 0)
-#define stbds_arrput(a,v) (stbds_arrmaybegrow(a,1), (a)[stbds_header(a)->length++] = (v))
-#define stbds_arrpush stbds_arrput // synonym
-#define stbds_arrpop(a) (stbds_header(a)->length--, (a)[stbds_header(a)->length])
-#define stbds_arraddn(a,n) ((void)(stbds_arraddnindex(a, n))) // deprecated, use one of the following instead:
-#define stbds_arraddnptr(a,n) (stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), &(a)[stbds_header(a)->length-(n)]) : (a))
-#define stbds_arraddnindex(a,n)(stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), stbds_header(a)->length-(n)) : stbds_arrlen(a))
-#define stbds_arraddnoff stbds_arraddnindex
-#define stbds_arrlast(a) ((a)[stbds_header(a)->length-1])
-#define stbds_arrfree(a) ((void) ((a) ? STBDS_FREE(NULL,stbds_header(a)) : (void)0), (a)=NULL)
-#define stbds_arrdel(a,i) stbds_arrdeln(a,i,1)
-#define stbds_arrdeln(a,i,n) (memmove(&(a)[i], &(a)[(i)+(n)], sizeof *(a) * (stbds_header(a)->length-(n)-(i))), stbds_header(a)->length -= (n))
-#define stbds_arrdelswap(a,i) ((a)[i] = stbds_arrlast(a), stbds_header(a)->length -= 1)
-#define stbds_arrinsn(a,i,n) (stbds_arraddn((a),(n)), memmove(&(a)[(i)+(n)], &(a)[i], sizeof *(a) * (stbds_header(a)->length-(n)-(i))))
-#define stbds_arrins(a,i,v) (stbds_arrinsn((a),(i),1), (a)[i]=(v))
-
-#define stbds_arrmaybegrow(a,n) ((!(a) || stbds_header(a)->length + (n) > stbds_header(a)->capacity) \
- ? (stbds_arrgrow(a,n,0),0) : 0)
-
-#define stbds_arrgrow(a,b,c) ((a) = stbds_arrgrowf_wrapper((a), sizeof *(a), (b), (c)))
-
-#define stbds_hmput(t, k, v) \
- ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, 0), \
- (t)[stbds_temp((t)-1)].key = (k), \
- (t)[stbds_temp((t)-1)].value = (v))
-
-#define stbds_hmputs(t, s) \
- ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), &(s).key, sizeof (s).key, STBDS_HM_BINARY), \
- (t)[stbds_temp((t)-1)] = (s))
-
-#define stbds_hmgeti(t,k) \
- ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_HM_BINARY), \
- stbds_temp((t)-1))
-
-#define stbds_hmgeti_ts(t,k,temp) \
- ((t) = stbds_hmget_key_ts_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, &(temp), STBDS_HM_BINARY), \
- (temp))
-
-#define stbds_hmgetp(t, k) \
- ((void) stbds_hmgeti(t,k), &(t)[stbds_temp((t)-1)])
-
-#define stbds_hmgetp_ts(t, k, temp) \
- ((void) stbds_hmgeti_ts(t,k,temp), &(t)[temp])
-
-#define stbds_hmdel(t,k) \
- (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_BINARY)),(t)?stbds_temp((t)-1):0)
-
-#define stbds_hmdefault(t, v) \
- ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1].value = (v))
-
-#define stbds_hmdefaults(t, s) \
- ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1] = (s))
-
-#define stbds_hmfree(p) \
- ((void) ((p) != NULL ? stbds_hmfree_func((p)-1,sizeof*(p)),0 : 0),(p)=NULL)
-
-#define stbds_hmgets(t, k) (*stbds_hmgetp(t,k))
-#define stbds_hmget(t, k) (stbds_hmgetp(t,k)->value)
-#define stbds_hmget_ts(t, k, temp) (stbds_hmgetp_ts(t,k,temp)->value)
-#define stbds_hmlen(t) ((t) ? (ptrdiff_t) stbds_header((t)-1)->length-1 : 0)
-#define stbds_hmlenu(t) ((t) ? stbds_header((t)-1)->length-1 : 0)
-#define stbds_hmgetp_null(t,k) (stbds_hmgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)])
-
-#define stbds_shput(t, k, v) \
- ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \
- (t)[stbds_temp((t)-1)].value = (v))
-
-#define stbds_shputi(t, k, v) \
- ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \
- (t)[stbds_temp((t)-1)].value = (v), stbds_temp((t)-1))
-
-#define stbds_shputs(t, s) \
- ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (s).key, sizeof (s).key, STBDS_HM_STRING), \
- (t)[stbds_temp((t)-1)] = (s), \
- (t)[stbds_temp((t)-1)].key = stbds_temp_key((t)-1)) // above line overwrites whole structure, so must rewrite key here if it was allocated internally
-
-#define stbds_pshput(t, p) \
- ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (p)->key, sizeof (p)->key, STBDS_HM_PTR_TO_STRING), \
- (t)[stbds_temp((t)-1)] = (p))
-
-#define stbds_shgeti(t,k) \
- ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \
- stbds_temp((t)-1))
-
-#define stbds_pshgeti(t,k) \
- ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_HM_PTR_TO_STRING), \
- stbds_temp((t)-1))
-
-#define stbds_shgetp(t, k) \
- ((void) stbds_shgeti(t,k), &(t)[stbds_temp((t)-1)])
-
-#define stbds_pshget(t, k) \
- ((void) stbds_pshgeti(t,k), (t)[stbds_temp((t)-1)])
-
-#define stbds_shdel(t,k) \
- (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_STRING)),(t)?stbds_temp((t)-1):0)
-#define stbds_pshdel(t,k) \
- (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_OFFSETOF(*(t),key), STBDS_HM_PTR_TO_STRING)),(t)?stbds_temp((t)-1):0)
-
-#define stbds_sh_new_arena(t) \
- ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_ARENA))
-#define stbds_sh_new_strdup(t) \
- ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_STRDUP))
-
-#define stbds_shdefault(t, v) stbds_hmdefault(t,v)
-#define stbds_shdefaults(t, s) stbds_hmdefaults(t,s)
-
-#define stbds_shfree stbds_hmfree
-#define stbds_shlenu stbds_hmlenu
-
-#define stbds_shgets(t, k) (*stbds_shgetp(t,k))
-#define stbds_shget(t, k) (stbds_shgetp(t,k)->value)
-#define stbds_shgetp_null(t,k) (stbds_shgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)])
-#define stbds_shlen stbds_hmlen
-
-typedef struct
-{
- size_t length;
- size_t capacity;
- void * hash_table;
- ptrdiff_t temp;
-} stbds_array_header;
-
-typedef struct stbds_string_block
-{
- struct stbds_string_block *next;
- char storage[8];
-} stbds_string_block;
-
-struct stbds_string_arena
-{
- stbds_string_block *storage;
- size_t remaining;
- unsigned char block;
- unsigned char mode; // this isn't used by the string arena itself
-};
-
-#define STBDS_HM_BINARY 0
-#define STBDS_HM_STRING 1
-
-enum
-{
- STBDS_SH_NONE,
- STBDS_SH_DEFAULT,
- STBDS_SH_STRDUP,
- STBDS_SH_ARENA
-};
-
-#ifdef __cplusplus
-// in C we use implicit assignment from these void*-returning functions to T*.
-// in C++ these templates make the same code work
-template<class T> static T * stbds_arrgrowf_wrapper(T *a, size_t elemsize, size_t addlen, size_t min_cap) {
- return (T*)stbds_arrgrowf((void *)a, elemsize, addlen, min_cap);
-}
-template<class T> static T * stbds_hmget_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) {
- return (T*)stbds_hmget_key((void*)a, elemsize, key, keysize, mode);
-}
-template<class T> static T * stbds_hmget_key_ts_wrapper(T *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode) {
- return (T*)stbds_hmget_key_ts((void*)a, elemsize, key, keysize, temp, mode);
-}
-template<class T> static T * stbds_hmput_default_wrapper(T *a, size_t elemsize) {
- return (T*)stbds_hmput_default((void *)a, elemsize);
-}
-template<class T> static T * stbds_hmput_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) {
- return (T*)stbds_hmput_key((void*)a, elemsize, key, keysize, mode);
-}
-template<class T> static T * stbds_hmdel_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode){
- return (T*)stbds_hmdel_key((void*)a, elemsize, key, keysize, keyoffset, mode);
-}
-template<class T> static T * stbds_shmode_func_wrapper(T *, size_t elemsize, int mode) {
- return (T*)stbds_shmode_func(elemsize, mode);
-}
-#else
-#define stbds_arrgrowf_wrapper stbds_arrgrowf
-#define stbds_hmget_key_wrapper stbds_hmget_key
-#define stbds_hmget_key_ts_wrapper stbds_hmget_key_ts
-#define stbds_hmput_default_wrapper stbds_hmput_default
-#define stbds_hmput_key_wrapper stbds_hmput_key
-#define stbds_hmdel_key_wrapper stbds_hmdel_key
-#define stbds_shmode_func_wrapper(t,e,m) stbds_shmode_func(e,m)
-#endif
-
-#endif // INCLUDE_STB_DS_H
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// IMPLEMENTATION
-//
-
-#ifdef STB_DS_IMPLEMENTATION
-#include <assert.h>
-#include <string.h>
-
-#ifndef STBDS_ASSERT
-#define STBDS_ASSERT_WAS_UNDEFINED
-#define STBDS_ASSERT(x) ((void) 0)
-#endif
-
-#ifdef STBDS_STATISTICS
-#define STBDS_STATS(x) x
-size_t stbds_array_grow;
-size_t stbds_hash_grow;
-size_t stbds_hash_shrink;
-size_t stbds_hash_rebuild;
-size_t stbds_hash_probes;
-size_t stbds_hash_alloc;
-size_t stbds_rehash_probes;
-size_t stbds_rehash_items;
-#else
-#define STBDS_STATS(x)
-#endif
-
-//
-// stbds_arr implementation
-//
-
-//int *prev_allocs[65536];
-//int num_prev;
-
-void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap)
-{
- stbds_array_header temp={0}; // force debugging
- void *b;
- size_t min_len = stbds_arrlen(a) + addlen;
- (void) sizeof(temp);
-
- // compute the minimum capacity needed
- if (min_len > min_cap)
- min_cap = min_len;
-
- if (min_cap <= stbds_arrcap(a))
- return a;
-
- // increase needed capacity to guarantee O(1) amortized
- if (min_cap < 2 * stbds_arrcap(a))
- min_cap = 2 * stbds_arrcap(a);
- else if (min_cap < 4)
- min_cap = 4;
-
- //if (num_prev < 65536) if (a) prev_allocs[num_prev++] = (int *) ((char *) a+1);
- //if (num_prev == 2201)
- // num_prev = num_prev;
- b = STBDS_REALLOC(NULL, (a) ? stbds_header(a) : 0, elemsize * min_cap + sizeof(stbds_array_header));
- //if (num_prev < 65536) prev_allocs[num_prev++] = (int *) (char *) b;
- b = (char *) b + sizeof(stbds_array_header);
- if (a == NULL) {
- stbds_header(b)->length = 0;
- stbds_header(b)->hash_table = 0;
- stbds_header(b)->temp = 0;
- } else {
- STBDS_STATS(++stbds_array_grow);
- }
- stbds_header(b)->capacity = min_cap;
-
- return b;
-}
-
-void stbds_arrfreef(void *a)
-{
- STBDS_FREE(NULL, stbds_header(a));
-}
-
-//
-// stbds_hm hash table implementation
-//
-
-#ifdef STBDS_INTERNAL_SMALL_BUCKET
-#define STBDS_BUCKET_LENGTH 4
-#else
-#define STBDS_BUCKET_LENGTH 8
-#endif
-
-#define STBDS_BUCKET_SHIFT (STBDS_BUCKET_LENGTH == 8 ? 3 : 2)
-#define STBDS_BUCKET_MASK (STBDS_BUCKET_LENGTH-1)
-#define STBDS_CACHE_LINE_SIZE 64
-
-#define STBDS_ALIGN_FWD(n,a) (((n) + (a) - 1) & ~((a)-1))
-
-typedef struct
-{
- size_t hash [STBDS_BUCKET_LENGTH];
- ptrdiff_t index[STBDS_BUCKET_LENGTH];
-} stbds_hash_bucket; // in 32-bit, this is one 64-byte cache line; in 64-bit, each array is one 64-byte cache line
-
-typedef struct
-{
- char * temp_key; // this MUST be the first field of the hash table
- size_t slot_count;
- size_t used_count;
- size_t used_count_threshold;
- size_t used_count_shrink_threshold;
- size_t tombstone_count;
- size_t tombstone_count_threshold;
- size_t seed;
- size_t slot_count_log2;
- stbds_string_arena string;
- stbds_hash_bucket *storage; // not a separate allocation, just 64-byte aligned storage after this struct
-} stbds_hash_index;
-
-#define STBDS_INDEX_EMPTY -1
-#define STBDS_INDEX_DELETED -2
-#define STBDS_INDEX_IN_USE(x) ((x) >= 0)
-
-#define STBDS_HASH_EMPTY 0
-#define STBDS_HASH_DELETED 1
-
-static size_t stbds_hash_seed=0x31415926;
-
-void stbds_rand_seed(size_t seed)
-{
- stbds_hash_seed = seed;
-}
-
-#define stbds_load_32_or_64(var, temp, v32, v64_hi, v64_lo) \
- temp = v64_lo ^ v32, temp <<= 16, temp <<= 16, temp >>= 16, temp >>= 16, /* discard if 32-bit */ \
- var = v64_hi, var <<= 16, var <<= 16, /* discard if 32-bit */ \
- var ^= temp ^ v32
-
-#define STBDS_SIZE_T_BITS ((sizeof (size_t)) * 8)
-
-static size_t stbds_probe_position(size_t hash, size_t slot_count, size_t slot_log2)
-{
- size_t pos;
- STBDS_NOTUSED(slot_log2);
- pos = hash & (slot_count-1);
- #ifdef STBDS_INTERNAL_BUCKET_START
- pos &= ~STBDS_BUCKET_MASK;
- #endif
- return pos;
-}
-
-static size_t stbds_log2(size_t slot_count)
-{
- size_t n=0;
- while (slot_count > 1) {
- slot_count >>= 1;
- ++n;
- }
- return n;
-}
-
-static stbds_hash_index *stbds_make_hash_index(size_t slot_count, stbds_hash_index *ot)
-{
- stbds_hash_index *t;
- t = (stbds_hash_index *) STBDS_REALLOC(NULL,0,(slot_count >> STBDS_BUCKET_SHIFT) * sizeof(stbds_hash_bucket) + sizeof(stbds_hash_index) + STBDS_CACHE_LINE_SIZE-1);
- t->storage = (stbds_hash_bucket *) STBDS_ALIGN_FWD((size_t) (t+1), STBDS_CACHE_LINE_SIZE);
- t->slot_count = slot_count;
- t->slot_count_log2 = stbds_log2(slot_count);
- t->tombstone_count = 0;
- t->used_count = 0;
-
- #if 0 // A1
- t->used_count_threshold = slot_count*12/16; // if 12/16th of table is occupied, grow
- t->tombstone_count_threshold = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
- t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink
- #elif 1 // A2
- //t->used_count_threshold = slot_count*12/16; // if 12/16th of table is occupied, grow
- //t->tombstone_count_threshold = slot_count* 3/16; // if tombstones are 3/16th of table, rebuild
- //t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink
-
- // compute without overflowing
- t->used_count_threshold = slot_count - (slot_count>>2);
- t->tombstone_count_threshold = (slot_count>>3) + (slot_count>>4);
- t->used_count_shrink_threshold = slot_count >> 2;
-
- #elif 0 // B1
- t->used_count_threshold = slot_count*13/16; // if 13/16th of table is occupied, grow
- t->tombstone_count_threshold = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
- t->used_count_shrink_threshold = slot_count* 5/16; // if table is only 5/16th full, shrink
- #else // C1
- t->used_count_threshold = slot_count*14/16; // if 14/16th of table is occupied, grow
- t->tombstone_count_threshold = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
- t->used_count_shrink_threshold = slot_count* 6/16; // if table is only 6/16th full, shrink
- #endif
- // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2
- // Note that the larger tables have high variance as they were run fewer times
- // A1 A2 B1 C1
- // 0.10ms : 0.10ms : 0.10ms : 0.11ms : 2,000 inserts creating 2K table
- // 0.96ms : 0.95ms : 0.97ms : 1.04ms : 20,000 inserts creating 20K table
- // 14.48ms : 14.46ms : 10.63ms : 11.00ms : 200,000 inserts creating 200K table
- // 195.74ms : 196.35ms : 203.69ms : 214.92ms : 2,000,000 inserts creating 2M table
- // 2193.88ms : 2209.22ms : 2285.54ms : 2437.17ms : 20,000,000 inserts creating 20M table
- // 65.27ms : 53.77ms : 65.33ms : 65.47ms : 500,000 inserts & deletes in 2K table
- // 72.78ms : 62.45ms : 71.95ms : 72.85ms : 500,000 inserts & deletes in 20K table
- // 89.47ms : 77.72ms : 96.49ms : 96.75ms : 500,000 inserts & deletes in 200K table
- // 97.58ms : 98.14ms : 97.18ms : 97.53ms : 500,000 inserts & deletes in 2M table
- // 118.61ms : 119.62ms : 120.16ms : 118.86ms : 500,000 inserts & deletes in 20M table
- // 192.11ms : 194.39ms : 196.38ms : 195.73ms : 500,000 inserts & deletes in 200M table
-
- if (slot_count <= STBDS_BUCKET_LENGTH)
- t->used_count_shrink_threshold = 0;
- // to avoid infinite loop, we need to guarantee that at least one slot is empty and will terminate probes
- STBDS_ASSERT(t->used_count_threshold + t->tombstone_count_threshold < t->slot_count);
- STBDS_STATS(++stbds_hash_alloc);
- if (ot) {
- t->string = ot->string;
- // reuse old seed so we can reuse old hashes so below "copy out old data" doesn't do any hashing
- t->seed = ot->seed;
- } else {
- size_t a,b,temp;
- memset(&t->string, 0, sizeof(t->string));
- t->seed = stbds_hash_seed;
- // LCG
- // in 32-bit, a = 2147001325 b = 715136305
- // in 64-bit, a = 2862933555777941757 b = 3037000493
- stbds_load_32_or_64(a,temp, 2147001325, 0x27bb2ee6, 0x87b0b0fd);
- stbds_load_32_or_64(b,temp, 715136305, 0, 0xb504f32d);
- stbds_hash_seed = stbds_hash_seed * a + b;
- }
-
- {
- size_t i,j;
- for (i=0; i < slot_count >> STBDS_BUCKET_SHIFT; ++i) {
- stbds_hash_bucket *b = &t->storage[i];
- for (j=0; j < STBDS_BUCKET_LENGTH; ++j)
- b->hash[j] = STBDS_HASH_EMPTY;
- for (j=0; j < STBDS_BUCKET_LENGTH; ++j)
- b->index[j] = STBDS_INDEX_EMPTY;
- }
- }
-
- // copy out the old data, if any
- if (ot) {
- size_t i,j;
- t->used_count = ot->used_count;
- for (i=0; i < ot->slot_count >> STBDS_BUCKET_SHIFT; ++i) {
- stbds_hash_bucket *ob = &ot->storage[i];
- for (j=0; j < STBDS_BUCKET_LENGTH; ++j) {
- if (STBDS_INDEX_IN_USE(ob->index[j])) {
- size_t hash = ob->hash[j];
- size_t pos = stbds_probe_position(hash, t->slot_count, t->slot_count_log2);
- size_t step = STBDS_BUCKET_LENGTH;
- STBDS_STATS(++stbds_rehash_items);
- for (;;) {
- size_t limit,z;
- stbds_hash_bucket *bucket;
- bucket = &t->storage[pos >> STBDS_BUCKET_SHIFT];
- STBDS_STATS(++stbds_rehash_probes);
-
- for (z=pos & STBDS_BUCKET_MASK; z < STBDS_BUCKET_LENGTH; ++z) {
- if (bucket->hash[z] == 0) {
- bucket->hash[z] = hash;
- bucket->index[z] = ob->index[j];
- goto done;
- }
- }
-
- limit = pos & STBDS_BUCKET_MASK;
- for (z = 0; z < limit; ++z) {
- if (bucket->hash[z] == 0) {
- bucket->hash[z] = hash;
- bucket->index[z] = ob->index[j];
- goto done;
- }
- }
-
- pos += step; // quadratic probing
- step += STBDS_BUCKET_LENGTH;
- pos &= (t->slot_count-1);
- }
- }
- done:
- ;
- }
- }
- }
-
- return t;
-}
-
-#define STBDS_ROTATE_LEFT(val, n) (((val) << (n)) | ((val) >> (STBDS_SIZE_T_BITS - (n))))
-#define STBDS_ROTATE_RIGHT(val, n) (((val) >> (n)) | ((val) << (STBDS_SIZE_T_BITS - (n))))
-
-size_t stbds_hash_string(char *str, size_t seed)
-{
- size_t hash = seed;
- while (*str)
- hash = STBDS_ROTATE_LEFT(hash, 9) + (unsigned char) *str++;
-
- // Thomas Wang 64-to-32 bit mix function, hopefully also works in 32 bits
- hash ^= seed;
- hash = (~hash) + (hash << 18);
- hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,31);
- hash = hash * 21;
- hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,11);
- hash += (hash << 6);
- hash ^= STBDS_ROTATE_RIGHT(hash,22);
- return hash+seed;
-}
-
-#ifdef STBDS_SIPHASH_2_4
-#define STBDS_SIPHASH_C_ROUNDS 2
-#define STBDS_SIPHASH_D_ROUNDS 4
-typedef int STBDS_SIPHASH_2_4_can_only_be_used_in_64_bit_builds[sizeof(size_t) == 8 ? 1 : -1];
-#endif
-
-#ifndef STBDS_SIPHASH_C_ROUNDS
-#define STBDS_SIPHASH_C_ROUNDS 1
-#endif
-#ifndef STBDS_SIPHASH_D_ROUNDS
-#define STBDS_SIPHASH_D_ROUNDS 1
-#endif
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4127) // conditional expression is constant, for do..while(0) and sizeof()==
-#endif
-
-static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed)
-{
- unsigned char *d = (unsigned char *) p;
- size_t i,j;
- size_t v0,v1,v2,v3, data;
-
- // hash that works on 32- or 64-bit registers without knowing which we have
- // (computes different results on 32-bit and 64-bit platform)
- // derived from siphash, but on 32-bit platforms very different as it uses 4 32-bit state not 4 64-bit
- v0 = ((((size_t) 0x736f6d65 << 16) << 16) + 0x70736575) ^ seed;
- v1 = ((((size_t) 0x646f7261 << 16) << 16) + 0x6e646f6d) ^ ~seed;
- v2 = ((((size_t) 0x6c796765 << 16) << 16) + 0x6e657261) ^ seed;
- v3 = ((((size_t) 0x74656462 << 16) << 16) + 0x79746573) ^ ~seed;
-
- #ifdef STBDS_TEST_SIPHASH_2_4
- // hardcoded with key material in the siphash test vectors
- v0 ^= 0x0706050403020100ull ^ seed;
- v1 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed;
- v2 ^= 0x0706050403020100ull ^ seed;
- v3 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed;
- #endif
-
- #define STBDS_SIPROUND() \
- do { \
- v0 += v1; v1 = STBDS_ROTATE_LEFT(v1, 13); v1 ^= v0; v0 = STBDS_ROTATE_LEFT(v0,STBDS_SIZE_T_BITS/2); \
- v2 += v3; v3 = STBDS_ROTATE_LEFT(v3, 16); v3 ^= v2; \
- v2 += v1; v1 = STBDS_ROTATE_LEFT(v1, 17); v1 ^= v2; v2 = STBDS_ROTATE_LEFT(v2,STBDS_SIZE_T_BITS/2); \
- v0 += v3; v3 = STBDS_ROTATE_LEFT(v3, 21); v3 ^= v0; \
- } while (0)
-
- for (i=0; i+sizeof(size_t) <= len; i += sizeof(size_t), d += sizeof(size_t)) {
- data = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
- data |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // discarded if size_t == 4
-
- v3 ^= data;
- for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)
- STBDS_SIPROUND();
- v0 ^= data;
- }
- data = len << (STBDS_SIZE_T_BITS-8);
- switch (len - i) {
- case 7: data |= ((size_t) d[6] << 24) << 24; // fall through
- case 6: data |= ((size_t) d[5] << 20) << 20; // fall through
- case 5: data |= ((size_t) d[4] << 16) << 16; // fall through
- case 4: data |= (d[3] << 24); // fall through
- case 3: data |= (d[2] << 16); // fall through
- case 2: data |= (d[1] << 8); // fall through
- case 1: data |= d[0]; // fall through
- case 0: break;
- }
- v3 ^= data;
- for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)
- STBDS_SIPROUND();
- v0 ^= data;
- v2 ^= 0xff;
- for (j=0; j < STBDS_SIPHASH_D_ROUNDS; ++j)
- STBDS_SIPROUND();
-
-#ifdef STBDS_SIPHASH_2_4
- return v0^v1^v2^v3;
-#else
- return v1^v2^v3; // slightly stronger since v0^v3 in above cancels out final round operation? I tweeted at the authors of SipHash about this but they didn't reply
-#endif
-}
-
-size_t stbds_hash_bytes(void *p, size_t len, size_t seed)
-{
-#ifdef STBDS_SIPHASH_2_4
- return stbds_siphash_bytes(p,len,seed);
-#else
- unsigned char *d = (unsigned char *) p;
-
- if (len == 4) {
- unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
- #if 0
- // HASH32-A Bob Jenkin's hash function w/o large constants
- hash ^= seed;
- hash -= (hash<<6);
- hash ^= (hash>>17);
- hash -= (hash<<9);
- hash ^= seed;
- hash ^= (hash<<4);
- hash -= (hash<<3);
- hash ^= (hash<<10);
- hash ^= (hash>>15);
- #elif 1
- // HASH32-BB Bob Jenkin's presumably-accidental version of Thomas Wang hash with rotates turned into shifts.
- // Note that converting these back to rotates makes it run a lot slower, presumably due to collisions, so I'm
- // not really sure what's going on.
- hash ^= seed;
- hash = (hash ^ 61) ^ (hash >> 16);
- hash = hash + (hash << 3);
- hash = hash ^ (hash >> 4);
- hash = hash * 0x27d4eb2d;
- hash ^= seed;
- hash = hash ^ (hash >> 15);
- #else // HASH32-C - Murmur3
- hash ^= seed;
- hash *= 0xcc9e2d51;
- hash = (hash << 17) | (hash >> 15);
- hash *= 0x1b873593;
- hash ^= seed;
- hash = (hash << 19) | (hash >> 13);
- hash = hash*5 + 0xe6546b64;
- hash ^= hash >> 16;
- hash *= 0x85ebca6b;
- hash ^= seed;
- hash ^= hash >> 13;
- hash *= 0xc2b2ae35;
- hash ^= hash >> 16;
- #endif
- // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2
- // Note that the larger tables have high variance as they were run fewer times
- // HASH32-A // HASH32-BB // HASH32-C
- // 0.10ms // 0.10ms // 0.10ms : 2,000 inserts creating 2K table
- // 0.96ms // 0.95ms // 0.99ms : 20,000 inserts creating 20K table
- // 14.69ms // 14.43ms // 14.97ms : 200,000 inserts creating 200K table
- // 199.99ms // 195.36ms // 202.05ms : 2,000,000 inserts creating 2M table
- // 2234.84ms // 2187.74ms // 2240.38ms : 20,000,000 inserts creating 20M table
- // 55.68ms // 53.72ms // 57.31ms : 500,000 inserts & deletes in 2K table
- // 63.43ms // 61.99ms // 65.73ms : 500,000 inserts & deletes in 20K table
- // 80.04ms // 77.96ms // 81.83ms : 500,000 inserts & deletes in 200K table
- // 100.42ms // 97.40ms // 102.39ms : 500,000 inserts & deletes in 2M table
- // 119.71ms // 120.59ms // 121.63ms : 500,000 inserts & deletes in 20M table
- // 185.28ms // 195.15ms // 187.74ms : 500,000 inserts & deletes in 200M table
- // 15.58ms // 14.79ms // 15.52ms : 200,000 inserts creating 200K table with varying key spacing
-
- return (((size_t) hash << 16 << 16) | hash) ^ seed;
- } else if (len == 8 && sizeof(size_t) == 8) {
- size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
- hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4
- hash ^= seed;
- hash = (~hash) + (hash << 21);
- hash ^= STBDS_ROTATE_RIGHT(hash,24);
- hash *= 265;
- hash ^= STBDS_ROTATE_RIGHT(hash,14);
- hash ^= seed;
- hash *= 21;
- hash ^= STBDS_ROTATE_RIGHT(hash,28);
- hash += (hash << 31);
- hash = (~hash) + (hash << 18);
- return hash;
- } else {
- return stbds_siphash_bytes(p,len,seed);
- }
-#endif
-}
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
-
-static int stbds_is_key_equal(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode, size_t i)
-{
- if (mode >= STBDS_HM_STRING)
- return 0==strcmp((char *) key, * (char **) ((char *) a + elemsize*i + keyoffset));
- else
- return 0==memcmp(key, (char *) a + elemsize*i + keyoffset, keysize);
-}
-
-#define STBDS_HASH_TO_ARR(x,elemsize) ((char*) (x) - (elemsize))
-#define STBDS_ARR_TO_HASH(x,elemsize) ((char*) (x) + (elemsize))
-
-#define stbds_hash_table(a) ((stbds_hash_index *) stbds_header(a)->hash_table)
-
-void stbds_hmfree_func(void *a, size_t elemsize)
-{
- if (a == NULL) return;
- if (stbds_hash_table(a) != NULL) {
- if (stbds_hash_table(a)->string.mode == STBDS_SH_STRDUP) {
- size_t i;
- // skip 0th element, which is default
- for (i=1; i < stbds_header(a)->length; ++i)
- STBDS_FREE(NULL, *(char**) ((char *) a + elemsize*i));
- }
- stbds_strreset(&stbds_hash_table(a)->string);
- }
- STBDS_FREE(NULL, stbds_header(a)->hash_table);
- STBDS_FREE(NULL, stbds_header(a));
-}
-
-static ptrdiff_t stbds_hm_find_slot(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode)
-{
- void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
- stbds_hash_index *table = stbds_hash_table(raw_a);
- size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed);
- size_t step = STBDS_BUCKET_LENGTH;
- size_t limit,i;
- size_t pos;
- stbds_hash_bucket *bucket;
-
- if (hash < 2) hash += 2; // stored hash values are forbidden from being 0, so we can detect empty slots
-
- pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2);
-
- for (;;) {
- STBDS_STATS(++stbds_hash_probes);
- bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
-
- // start searching from pos to end of bucket, this should help performance on small hash tables that fit in cache
- for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) {
- if (bucket->hash[i] == hash) {
- if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
- return (pos & ~STBDS_BUCKET_MASK)+i;
- }
- } else if (bucket->hash[i] == STBDS_HASH_EMPTY) {
- return -1;
- }
- }
-
- // search from beginning of bucket to pos
- limit = pos & STBDS_BUCKET_MASK;
- for (i = 0; i < limit; ++i) {
- if (bucket->hash[i] == hash) {
- if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
- return (pos & ~STBDS_BUCKET_MASK)+i;
- }
- } else if (bucket->hash[i] == STBDS_HASH_EMPTY) {
- return -1;
- }
- }
-
- // quadratic probing
- pos += step;
- step += STBDS_BUCKET_LENGTH;
- pos &= (table->slot_count-1);
- }
- /* NOTREACHED */
-}
-
-void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode)
-{
- size_t keyoffset = 0;
- if (a == NULL) {
- // make it non-empty so we can return a temp
- a = stbds_arrgrowf(0, elemsize, 0, 1);
- stbds_header(a)->length += 1;
- memset(a, 0, elemsize);
- *temp = STBDS_INDEX_EMPTY;
- // adjust a to point after the default element
- return STBDS_ARR_TO_HASH(a,elemsize);
- } else {
- stbds_hash_index *table;
- void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
- // adjust a to point to the default element
- table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;
- if (table == 0) {
- *temp = -1;
- } else {
- ptrdiff_t slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode);
- if (slot < 0) {
- *temp = STBDS_INDEX_EMPTY;
- } else {
- stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
- *temp = b->index[slot & STBDS_BUCKET_MASK];
- }
- }
- return a;
- }
-}
-
-void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode)
-{
- ptrdiff_t temp;
- void *p = stbds_hmget_key_ts(a, elemsize, key, keysize, &temp, mode);
- stbds_temp(STBDS_HASH_TO_ARR(p,elemsize)) = temp;
- return p;
-}
-
-void * stbds_hmput_default(void *a, size_t elemsize)
-{
- // three cases:
- // a is NULL <- allocate
- // a has a hash table but no entries, because of shmode <- grow
- // a has entries <- do nothing
- if (a == NULL || stbds_header(STBDS_HASH_TO_ARR(a,elemsize))->length == 0) {
- a = stbds_arrgrowf(a ? STBDS_HASH_TO_ARR(a,elemsize) : NULL, elemsize, 0, 1);
- stbds_header(a)->length += 1;
- memset(a, 0, elemsize);
- a=STBDS_ARR_TO_HASH(a,elemsize);
- }
- return a;
-}
-
-static char *stbds_strdup(char *str);
-
-void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode)
-{
- size_t keyoffset=0;
- void *raw_a;
- stbds_hash_index *table;
-
- if (a == NULL) {
- a = stbds_arrgrowf(0, elemsize, 0, 1);
- memset(a, 0, elemsize);
- stbds_header(a)->length += 1;
- // adjust a to point AFTER the default element
- a = STBDS_ARR_TO_HASH(a,elemsize);
- }
-
- // adjust a to point to the default element
- raw_a = a;
- a = STBDS_HASH_TO_ARR(a,elemsize);
-
- table = (stbds_hash_index *) stbds_header(a)->hash_table;
-
- if (table == NULL || table->used_count >= table->used_count_threshold) {
- stbds_hash_index *nt;
- size_t slot_count;
-
- slot_count = (table == NULL) ? STBDS_BUCKET_LENGTH : table->slot_count*2;
- nt = stbds_make_hash_index(slot_count, table);
- if (table)
- STBDS_FREE(NULL, table);
- else
- nt->string.mode = mode >= STBDS_HM_STRING ? STBDS_SH_DEFAULT : 0;
- stbds_header(a)->hash_table = table = nt;
- STBDS_STATS(++stbds_hash_grow);
- }
-
- // we iterate hash table explicitly because we want to track if we saw a tombstone
- {
- size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed);
- size_t step = STBDS_BUCKET_LENGTH;
- size_t pos;
- ptrdiff_t tombstone = -1;
- stbds_hash_bucket *bucket;
-
- // stored hash values are forbidden from being 0, so we can detect empty slots to early out quickly
- if (hash < 2) hash += 2;
-
- pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2);
-
- for (;;) {
- size_t limit, i;
- STBDS_STATS(++stbds_hash_probes);
- bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
-
- // start searching from pos to end of bucket
- for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) {
- if (bucket->hash[i] == hash) {
- if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
- stbds_temp(a) = bucket->index[i];
- if (mode >= STBDS_HM_STRING)
- stbds_temp_key(a) = * (char **) ((char *) raw_a + elemsize*bucket->index[i] + keyoffset);
- return STBDS_ARR_TO_HASH(a,elemsize);
- }
- } else if (bucket->hash[i] == 0) {
- pos = (pos & ~STBDS_BUCKET_MASK) + i;
- goto found_empty_slot;
- } else if (tombstone < 0) {
- if (bucket->index[i] == STBDS_INDEX_DELETED)
- tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i);
- }
- }
-
- // search from beginning of bucket to pos
- limit = pos & STBDS_BUCKET_MASK;
- for (i = 0; i < limit; ++i) {
- if (bucket->hash[i] == hash) {
- if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
- stbds_temp(a) = bucket->index[i];
- return STBDS_ARR_TO_HASH(a,elemsize);
- }
- } else if (bucket->hash[i] == 0) {
- pos = (pos & ~STBDS_BUCKET_MASK) + i;
- goto found_empty_slot;
- } else if (tombstone < 0) {
- if (bucket->index[i] == STBDS_INDEX_DELETED)
- tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i);
- }
- }
-
- // quadratic probing
- pos += step;
- step += STBDS_BUCKET_LENGTH;
- pos &= (table->slot_count-1);
- }
- found_empty_slot:
- if (tombstone >= 0) {
- pos = tombstone;
- --table->tombstone_count;
- }
- ++table->used_count;
-
- {
- ptrdiff_t i = (ptrdiff_t) stbds_arrlen(a);
- // we want to do stbds_arraddn(1), but we can't use the macros since we don't have something of the right type
- if ((size_t) i+1 > stbds_arrcap(a))
- *(void **) &a = stbds_arrgrowf(a, elemsize, 1, 0);
- raw_a = STBDS_ARR_TO_HASH(a,elemsize);
-
- STBDS_ASSERT((size_t) i+1 <= stbds_arrcap(a));
- stbds_header(a)->length = i+1;
- bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
- bucket->hash[pos & STBDS_BUCKET_MASK] = hash;
- bucket->index[pos & STBDS_BUCKET_MASK] = i-1;
- stbds_temp(a) = i-1;
-
- switch (table->string.mode) {
- case STBDS_SH_STRDUP: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_strdup((char*) key); break;
- case STBDS_SH_ARENA: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_stralloc(&table->string, (char*)key); break;
- case STBDS_SH_DEFAULT: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = (char *) key; break;
- default: memcpy((char *) a + elemsize*i, key, keysize); break;
- }
- }
- return STBDS_ARR_TO_HASH(a,elemsize);
- }
-}
-
-void * stbds_shmode_func(size_t elemsize, int mode)
-{
- void *a = stbds_arrgrowf(0, elemsize, 0, 1);
- stbds_hash_index *h;
- memset(a, 0, elemsize);
- stbds_header(a)->length = 1;
- stbds_header(a)->hash_table = h = (stbds_hash_index *) stbds_make_hash_index(STBDS_BUCKET_LENGTH, NULL);
- h->string.mode = (unsigned char) mode;
- return STBDS_ARR_TO_HASH(a,elemsize);
-}
-
-void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode)
-{
- if (a == NULL) {
- return 0;
- } else {
- stbds_hash_index *table;
- void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
- table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;
- stbds_temp(raw_a) = 0;
- if (table == 0) {
- return a;
- } else {
- ptrdiff_t slot;
- slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode);
- if (slot < 0)
- return a;
- else {
- stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
- int i = slot & STBDS_BUCKET_MASK;
- ptrdiff_t old_index = b->index[i];
- ptrdiff_t final_index = (ptrdiff_t) stbds_arrlen(raw_a)-1-1; // minus one for the raw_a vs a, and minus one for 'last'
- STBDS_ASSERT(slot < (ptrdiff_t) table->slot_count);
- --table->used_count;
- ++table->tombstone_count;
- stbds_temp(raw_a) = 1;
- STBDS_ASSERT(table->used_count >= 0);
- //STBDS_ASSERT(table->tombstone_count < table->slot_count/4);
- b->hash[i] = STBDS_HASH_DELETED;
- b->index[i] = STBDS_INDEX_DELETED;
-
- if (mode == STBDS_HM_STRING && table->string.mode == STBDS_SH_STRDUP)
- STBDS_FREE(NULL, *(char**) ((char *) a+elemsize*old_index));
-
- // if indices are the same, memcpy is a no-op, but back-pointer-fixup will fail, so skip
- if (old_index != final_index) {
- // swap delete
- memmove((char*) a + elemsize*old_index, (char*) a + elemsize*final_index, elemsize);
-
- // now find the slot for the last element
- if (mode == STBDS_HM_STRING)
- slot = stbds_hm_find_slot(a, elemsize, *(char**) ((char *) a+elemsize*old_index + keyoffset), keysize, keyoffset, mode);
- else
- slot = stbds_hm_find_slot(a, elemsize, (char* ) a+elemsize*old_index + keyoffset, keysize, keyoffset, mode);
- STBDS_ASSERT(slot >= 0);
- b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
- i = slot & STBDS_BUCKET_MASK;
- STBDS_ASSERT(b->index[i] == final_index);
- b->index[i] = old_index;
- }
- stbds_header(raw_a)->length -= 1;
-
- if (table->used_count < table->used_count_shrink_threshold && table->slot_count > STBDS_BUCKET_LENGTH) {
- stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count>>1, table);
- STBDS_FREE(NULL, table);
- STBDS_STATS(++stbds_hash_shrink);
- } else if (table->tombstone_count > table->tombstone_count_threshold) {
- stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count , table);
- STBDS_FREE(NULL, table);
- STBDS_STATS(++stbds_hash_rebuild);
- }
-
- return a;
- }
- }
- }
- /* NOTREACHED */
-}
-
-static char *stbds_strdup(char *str)
-{
- // to keep replaceable allocator simple, we don't want to use strdup.
- // rolling our own also avoids problem of strdup vs _strdup
- size_t len = strlen(str)+1;
- char *p = (char*) STBDS_REALLOC(NULL, 0, len);
- memmove(p, str, len);
- return p;
-}
-
-#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MIN
-#define STBDS_STRING_ARENA_BLOCKSIZE_MIN 512u
-#endif
-#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MAX
-#define STBDS_STRING_ARENA_BLOCKSIZE_MAX (1u<<20)
-#endif
-
-char *stbds_stralloc(stbds_string_arena *a, char *str)
-{
- char *p;
- size_t len = strlen(str)+1;
- if (len > a->remaining) {
- // compute the next blocksize
- size_t blocksize = a->block;
-
- // size is 512, 512, 1024, 1024, 2048, 2048, 4096, 4096, etc., so that
- // there are log(SIZE) allocations to free when we destroy the table
- blocksize = (size_t) (STBDS_STRING_ARENA_BLOCKSIZE_MIN) << (blocksize>>1);
-
- // if size is under 1M, advance to next blocktype
- if (blocksize < (size_t)(STBDS_STRING_ARENA_BLOCKSIZE_MAX))
- ++a->block;
-
- if (len > blocksize) {
- // if string is larger than blocksize, then just allocate the full size.
- // note that we still advance string_block so block size will continue
- // increasing, so e.g. if somebody only calls this with 1000-long strings,
- // eventually the arena will start doubling and handling those as well
- stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + len);
- memmove(sb->storage, str, len);
- if (a->storage) {
- // insert it after the first element, so that we don't waste the space there
- sb->next = a->storage->next;
- a->storage->next = sb;
- } else {
- sb->next = 0;
- a->storage = sb;
- a->remaining = 0; // this is redundant, but good for clarity
- }
- return sb->storage;
- } else {
- stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + blocksize);
- sb->next = a->storage;
- a->storage = sb;
- a->remaining = blocksize;
- }
- }
-
- STBDS_ASSERT(len <= a->remaining);
- p = a->storage->storage + a->remaining - len;
- a->remaining -= len;
- memmove(p, str, len);
- return p;
-}
-
-void stbds_strreset(stbds_string_arena *a)
-{
- stbds_string_block *x,*y;
- x = a->storage;
- while (x) {
- y = x->next;
- STBDS_FREE(NULL, x);
- x = y;
- }
- memset(a, 0, sizeof(*a));
-}
-
-#endif
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// UNIT TESTS
-//
-
-#ifdef STBDS_UNIT_TESTS
-#include <stdio.h>
-#ifdef STBDS_ASSERT_WAS_UNDEFINED
-#undef STBDS_ASSERT
-#endif
-#ifndef STBDS_ASSERT
-#define STBDS_ASSERT assert
-#include <assert.h>
-#endif
-
-typedef struct { int key,b,c,d; } stbds_struct;
-typedef struct { int key[2],b,c,d; } stbds_struct2;
-
-static char buffer[256];
-char *strkey(int n)
-{
-#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)
- sprintf_s(buffer, sizeof(buffer), "test_%d", n);
-#else
- sprintf(buffer, "test_%d", n);
-#endif
- return buffer;
-}
-
-void stbds_unit_tests(void)
-{
-#if defined(_MSC_VER) && _MSC_VER <= 1200 && defined(__cplusplus)
- // VC6 C++ doesn't like the template<> trick on unnamed structures, so do nothing!
- STBDS_ASSERT(0);
-#else
- const int testsize = 100000;
- const int testsize2 = testsize/20;
- int *arr=NULL;
- struct { int key; int value; } *intmap = NULL;
- struct { char *key; int value; } *strmap = NULL, s;
- struct { stbds_struct key; int value; } *map = NULL;
- stbds_struct *map2 = NULL;
- stbds_struct2 *map3 = NULL;
- stbds_string_arena sa = { 0 };
- int key3[2] = { 1,2 };
- ptrdiff_t temp;
-
- int i,j;
-
- STBDS_ASSERT(arrlen(arr)==0);
- for (i=0; i < 20000; i += 50) {
- for (j=0; j < i; ++j)
- arrpush(arr,j);
- arrfree(arr);
- }
-
- for (i=0; i < 4; ++i) {
- arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
- arrdel(arr,i);
- arrfree(arr);
- arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
- arrdelswap(arr,i);
- arrfree(arr);
- }
-
- for (i=0; i < 5; ++i) {
- arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
- stbds_arrins(arr,i,5);
- STBDS_ASSERT(arr[i] == 5);
- if (i < 4)
- STBDS_ASSERT(arr[4] == 4);
- arrfree(arr);
- }
-
- i = 1;
- STBDS_ASSERT(hmgeti(intmap,i) == -1);
- hmdefault(intmap, -2);
- STBDS_ASSERT(hmgeti(intmap, i) == -1);
- STBDS_ASSERT(hmget (intmap, i) == -2);
- for (i=0; i < testsize; i+=2)
- hmput(intmap, i, i*5);
- for (i=0; i < testsize; i+=1) {
- if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 );
- else STBDS_ASSERT(hmget(intmap, i) == i*5);
- if (i & 1) STBDS_ASSERT(hmget_ts(intmap, i, temp) == -2 );
- else STBDS_ASSERT(hmget_ts(intmap, i, temp) == i*5);
- }
- for (i=0; i < testsize; i+=2)
- hmput(intmap, i, i*3);
- for (i=0; i < testsize; i+=1)
- if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 );
- else STBDS_ASSERT(hmget(intmap, i) == i*3);
- for (i=2; i < testsize; i+=4)
- hmdel(intmap, i); // delete half the entries
- for (i=0; i < testsize; i+=1)
- if (i & 3) STBDS_ASSERT(hmget(intmap, i) == -2 );
- else STBDS_ASSERT(hmget(intmap, i) == i*3);
- for (i=0; i < testsize; i+=1)
- hmdel(intmap, i); // delete the rest of the entries
- for (i=0; i < testsize; i+=1)
- STBDS_ASSERT(hmget(intmap, i) == -2 );
- hmfree(intmap);
- for (i=0; i < testsize; i+=2)
- hmput(intmap, i, i*3);
- hmfree(intmap);
-
- #if defined(__clang__) || defined(__GNUC__)
- #ifndef __cplusplus
- intmap = NULL;
- hmput(intmap, 15, 7);
- hmput(intmap, 11, 3);
- hmput(intmap, 9, 5);
- STBDS_ASSERT(hmget(intmap, 9) == 5);
- STBDS_ASSERT(hmget(intmap, 11) == 3);
- STBDS_ASSERT(hmget(intmap, 15) == 7);
- #endif
- #endif
-
- for (i=0; i < testsize; ++i)
- stralloc(&sa, strkey(i));
- strreset(&sa);
-
- {
- s.key = "a", s.value = 1;
- shputs(strmap, s);
- STBDS_ASSERT(*strmap[0].key == 'a');
- STBDS_ASSERT(strmap[0].key == s.key);
- STBDS_ASSERT(strmap[0].value == s.value);
- shfree(strmap);
- }
-
- {
- s.key = "a", s.value = 1;
- sh_new_strdup(strmap);
- shputs(strmap, s);
- STBDS_ASSERT(*strmap[0].key == 'a');
- STBDS_ASSERT(strmap[0].key != s.key);
- STBDS_ASSERT(strmap[0].value == s.value);
- shfree(strmap);
- }
-
- {
- s.key = "a", s.value = 1;
- sh_new_arena(strmap);
- shputs(strmap, s);
- STBDS_ASSERT(*strmap[0].key == 'a');
- STBDS_ASSERT(strmap[0].key != s.key);
- STBDS_ASSERT(strmap[0].value == s.value);
- shfree(strmap);
- }
-
- for (j=0; j < 2; ++j) {
- STBDS_ASSERT(shgeti(strmap,"foo") == -1);
- if (j == 0)
- sh_new_strdup(strmap);
- else
- sh_new_arena(strmap);
- STBDS_ASSERT(shgeti(strmap,"foo") == -1);
- shdefault(strmap, -2);
- STBDS_ASSERT(shgeti(strmap,"foo") == -1);
- for (i=0; i < testsize; i+=2)
- shput(strmap, strkey(i), i*3);
- for (i=0; i < testsize; i+=1)
- if (i & 1) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
- else STBDS_ASSERT(shget(strmap, strkey(i)) == i*3);
- for (i=2; i < testsize; i+=4)
- shdel(strmap, strkey(i)); // delete half the entries
- for (i=0; i < testsize; i+=1)
- if (i & 3) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
- else STBDS_ASSERT(shget(strmap, strkey(i)) == i*3);
- for (i=0; i < testsize; i+=1)
- shdel(strmap, strkey(i)); // delete the rest of the entries
- for (i=0; i < testsize; i+=1)
- STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
- shfree(strmap);
- }
-
- {
- struct { char *key; char value; } *hash = NULL;
- char name[4] = "jen";
- shput(hash, "bob" , 'h');
- shput(hash, "sally" , 'e');
- shput(hash, "fred" , 'l');
- shput(hash, "jen" , 'x');
- shput(hash, "doug" , 'o');
-
- shput(hash, name , 'l');
- shfree(hash);
- }
-
- for (i=0; i < testsize; i += 2) {
- stbds_struct s = { i,i*2,i*3,i*4 };
- hmput(map, s, i*5);
- }
-
- for (i=0; i < testsize; i += 1) {
- stbds_struct s = { i,i*2,i*3 ,i*4 };
- stbds_struct t = { i,i*2,i*3+1,i*4 };
- if (i & 1) STBDS_ASSERT(hmget(map, s) == 0);
- else STBDS_ASSERT(hmget(map, s) == i*5);
- if (i & 1) STBDS_ASSERT(hmget_ts(map, s, temp) == 0);
- else STBDS_ASSERT(hmget_ts(map, s, temp) == i*5);
- //STBDS_ASSERT(hmget(map, t.key) == 0);
- }
-
- for (i=0; i < testsize; i += 2) {
- stbds_struct s = { i,i*2,i*3,i*4 };
- hmputs(map2, s);
- }
- hmfree(map);
-
- for (i=0; i < testsize; i += 1) {
- stbds_struct s = { i,i*2,i*3,i*4 };
- stbds_struct t = { i,i*2,i*3+1,i*4 };
- if (i & 1) STBDS_ASSERT(hmgets(map2, s.key).d == 0);
- else STBDS_ASSERT(hmgets(map2, s.key).d == i*4);
- //STBDS_ASSERT(hmgetp(map2, t.key) == 0);
- }
- hmfree(map2);
-
- for (i=0; i < testsize; i += 2) {
- stbds_struct2 s = { { i,i*2 }, i*3,i*4, i*5 };
- hmputs(map3, s);
- }
- for (i=0; i < testsize; i += 1) {
- stbds_struct2 s = { { i,i*2}, i*3, i*4, i*5 };
- stbds_struct2 t = { { i,i*2}, i*3+1, i*4, i*5 };
- if (i & 1) STBDS_ASSERT(hmgets(map3, s.key).d == 0);
- else STBDS_ASSERT(hmgets(map3, s.key).d == i*5);
- //STBDS_ASSERT(hmgetp(map3, t.key) == 0);
- }
-#endif
-}
-#endif
-
-
-/*
-------------------------------------------------------------------------------
-This software is available under 2 licenses -- choose whichever you prefer.
-------------------------------------------------------------------------------
-ALTERNATIVE A - MIT License
-Copyright (c) 2019 Sean Barrett
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-------------------------------------------------------------------------------
-ALTERNATIVE B - Public Domain (www.unlicense.org)
-This is free and unencumbered software released into the public domain.
-Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
-software, either in source code form or as a compiled binary, for any purpose,
-commercial or non-commercial, and by any means.
-In jurisdictions that recognize copyright laws, the author or authors of this
-software dedicate any and all copyright interest in the software to the public
-domain. We make this dedication for the benefit of the public at large and to
-the detriment of our heirs and successors. We intend this dedication to be an
-overt act of relinquishment in perpetuity of all present and future rights to
-this software under copyright law.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------------
-*/
+++ /dev/null
-#ifndef ONYXTYPES_H
-#define ONYXTYPES_H
-
-#include "bh.h"
-
-#define POINTER_SIZE 4
-
-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 OnyxToken* token;
-
- struct AstTyped** initial_value;
- i32 use_through_pointer_index;
- b32 included_through_use : 1;
- b32 used : 1;
-
- bh_arr(struct AstTyped *) meta_tags;
-} StructMember;
-
-typedef struct TypeWithOffset TypeWithOffset;
-struct TypeWithOffset {
- Type* type;
- u32 offset;
-};
-
-typedef enum StructProcessingStatus {
- SPS_Start,
- SPS_Members_Done,
- SPS_Uses_Done,
-} StructProcessingStatus;
-
-#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; \
- Table(StructMember *) members; \
- bh_arr(StructMember *) memarr; \
- bh_arr(struct AstPolySolution) poly_sln; \
- bh_arr(TypeWithOffset) linear_members; \
- struct AstType *constructed_from; \
- bh_arr(struct AstTyped *) meta_tags; \
- StructProcessingStatus status; \
- }) \
- TYPE_KIND(PolyStruct, struct { \
- char* name; \
- bh_arr(struct AstTyped *) meta_tags; \
- }) \
- TYPE_KIND(Compound, struct { \
- u32 count; \
- u32 size; \
- bh_arr(TypeWithOffset) linear_members; \
- Type* types[]; \
- }) \
- TYPE_KIND(Array, struct { Type* elem; u32 size; u32 count; }) \
- TYPE_KIND(Slice, struct { Type *elem; }) \
- TYPE_KIND(DynArray, struct { Type *elem; }) \
- TYPE_KIND(VarArgs, struct { Type *elem; }) \
- TYPE_KIND(Enum, struct { \
- char* name; \
- Type* backing; \
- b32 is_flags; \
- }) \
- TYPE_KIND(Distinct, struct { \
- char* name; \
- Type* base_type; \
- })
-
-
-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;
-struct AstStructLiteral;
-
-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_implicit_type_of_struct_literal(bh_allocator alloc, struct AstStructLiteral* lit);
-
-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);
-b32 type_struct_member_apply_use(bh_allocator alloc, Type *s_type, StructMember *smem);
-
-const char* type_get_unique_name(Type* type);
-const char* type_get_name(Type* type);
-u32 type_get_alignment_log2(Type* type);
-Type* type_get_contained_type(Type* type);
-
-b32 type_lookup_member(Type* type, char* member, StructMember* smem);
-b32 type_lookup_member_by_idx(Type* type, i32 idx, StructMember* smem);
-
-i32 type_linear_member_count(Type* type);
-b32 type_linear_member_lookup(Type* type, i32 idx, TypeWithOffset* two);
-i32 type_get_idx_of_linear_member_with_offset(Type* type, u32 offset);
-
-b32 type_struct_is_simple(Type* type);
-
-b32 type_is_pointer(Type* type);
-b32 type_is_rawptr(Type* type);
-b32 type_is_array(Type* tyoe);
-b32 type_is_struct(Type* type);
-b32 type_is_bool(Type* type);
-b32 type_is_small_integer(Type* type);
-b32 type_is_integer(Type* type);
-b32 type_is_numeric(Type* type);
-b32 type_is_compound(Type* type);
-b32 type_is_simd(Type* type);
-b32 type_results_in_void(Type* type);
-b32 type_is_array_accessible(Type* type);
-b32 type_is_structlike(Type* type);
-b32 type_is_structlike_strict(Type* type);
-u32 type_structlike_mem_count(Type* type);
-u32 type_structlike_is_simple(Type* type);
-b32 type_is_sl_constructable(Type* type);
-b32 type_struct_constructed_from_poly_struct(Type* struct_type, struct AstType* from);
-
-#endif // #ifndef ONYX_TYPES
+++ /dev/null
-#include "bh.h"
-
-#include "astnodes.h"
-
-extern bh_scratch global_scratch;
-extern bh_allocator global_scratch_allocator;
-
-extern bh_managed_heap global_heap;
-extern bh_allocator global_heap_allocator;
-
-const char* onyx_ast_node_kind_string(AstKind kind);
-
-Package* package_lookup(char* package_name);
-Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc, OnyxFilePos pos);
-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);
-AstNode* try_symbol_raw_resolve_from_type(Type *type, char* symbol);
-Scope *get_scope_from_node(AstNode *node);
-Scope *get_scope_from_node_or_create(AstNode *node);
-
-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);
-
-u32 levenshtein_distance(const char *str1, const char *str2);
-char *find_closest_symbol_in_scope_and_parents(Scope *scope, char *sym);
-char *find_closest_symbol_in_node(AstNode *node, char *sym);
-
-extern AstTyped node_that_signals_a_yield;
-extern AstTyped node_that_signals_failure;
+++ /dev/null
-#ifndef ONYXWASM_H
-#define ONYXWASM_H
-
-#include "bh.h"
-
-#include "astnodes.h"
-#include "errors.h"
-
-typedef u8 WasmType;
-
-typedef struct WasmFuncType {
- // NOTE: For now, WASM only allows for 1 return value.
- // This may be lifted in the future.
- i32 param_count;
- WasmType return_type;
- WasmType param_types[];
-} WasmFuncType;
-
-#define SIMD_INSTR_MASK 0x10000
-#define EXT_INSTR_MASK 0x20000
-#define ATOMIC_INSTR_MASK 0x40000
-
-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_INIT = EXT_INSTR_MASK | 0x08,
- WI_MEMORY_COPY = EXT_INSTR_MASK | 0x0a,
- WI_MEMORY_FILL = EXT_INSTR_MASK | 0x0b,
-
- WI_ATOMIC_NOTIFY = ATOMIC_INSTR_MASK | 0x00,
- WI_ATOMIC_WAIT32 = ATOMIC_INSTR_MASK | 0x01,
- WI_ATOMIC_WAIT64 = ATOMIC_INSTR_MASK | 0x02,
-
- WI_ATOMIC_FENCE = ATOMIC_INSTR_MASK | 0x03,
-
- WI_ATOMIC_I32_LOAD = ATOMIC_INSTR_MASK | 0x10,
- WI_ATOMIC_I64_LOAD = ATOMIC_INSTR_MASK | 0x11,
- WI_ATOMIC_I32_LOAD8_U = ATOMIC_INSTR_MASK | 0x12,
- WI_ATOMIC_I32_LOAD16_U = ATOMIC_INSTR_MASK | 0x13,
- WI_ATOMIC_I64_LOAD8_U = ATOMIC_INSTR_MASK | 0x14,
- WI_ATOMIC_I64_LOAD16_U = ATOMIC_INSTR_MASK | 0x15,
- WI_ATOMIC_I64_LOAD32_U = ATOMIC_INSTR_MASK | 0x16,
-
- WI_ATOMIC_I32_STORE = ATOMIC_INSTR_MASK | 0x17,
- WI_ATOMIC_I64_STORE = ATOMIC_INSTR_MASK | 0x18,
- WI_ATOMIC_I32_STORE8 = ATOMIC_INSTR_MASK | 0x19,
- WI_ATOMIC_I32_STORE16 = ATOMIC_INSTR_MASK | 0x1a,
- WI_ATOMIC_I64_STORE8 = ATOMIC_INSTR_MASK | 0x1b,
- WI_ATOMIC_I64_STORE16 = ATOMIC_INSTR_MASK | 0x1c,
- WI_ATOMIC_I64_STORE32 = ATOMIC_INSTR_MASK | 0x1d,
-
- WI_ATOMIC_I32_ADD = ATOMIC_INSTR_MASK | 0x1e,
- WI_ATOMIC_I64_ADD = ATOMIC_INSTR_MASK | 0x1f,
- WI_ATOMIC_I32_ADD8_U = ATOMIC_INSTR_MASK | 0x20,
- WI_ATOMIC_I32_ADD16_U = ATOMIC_INSTR_MASK | 0x21,
- WI_ATOMIC_I64_ADD8_U = ATOMIC_INSTR_MASK | 0x22,
- WI_ATOMIC_I64_ADD16_U = ATOMIC_INSTR_MASK | 0x23,
- WI_ATOMIC_I64_ADD32_U = ATOMIC_INSTR_MASK | 0x24,
-
- WI_ATOMIC_I32_SUB = ATOMIC_INSTR_MASK | 0x25,
- WI_ATOMIC_I64_SUB = ATOMIC_INSTR_MASK | 0x26,
- WI_ATOMIC_I32_SUB8_U = ATOMIC_INSTR_MASK | 0x27,
- WI_ATOMIC_I32_SUB16_U = ATOMIC_INSTR_MASK | 0x28,
- WI_ATOMIC_I64_SUB8_U = ATOMIC_INSTR_MASK | 0x29,
- WI_ATOMIC_I64_SUB16_U = ATOMIC_INSTR_MASK | 0x2a,
- WI_ATOMIC_I64_SUB32_U = ATOMIC_INSTR_MASK | 0x2b,
-
- WI_ATOMIC_I32_AND = ATOMIC_INSTR_MASK | 0x2c,
- WI_ATOMIC_I64_AND = ATOMIC_INSTR_MASK | 0x2d,
- WI_ATOMIC_I32_AND8_U = ATOMIC_INSTR_MASK | 0x2e,
- WI_ATOMIC_I32_AND16_U = ATOMIC_INSTR_MASK | 0x2f,
- WI_ATOMIC_I64_AND8_U = ATOMIC_INSTR_MASK | 0x30,
- WI_ATOMIC_I64_AND16_U = ATOMIC_INSTR_MASK | 0x31,
- WI_ATOMIC_I64_AND32_U = ATOMIC_INSTR_MASK | 0x32,
-
- WI_ATOMIC_I32_OR = ATOMIC_INSTR_MASK | 0x33,
- WI_ATOMIC_I64_OR = ATOMIC_INSTR_MASK | 0x34,
- WI_ATOMIC_I32_OR8_U = ATOMIC_INSTR_MASK | 0x35,
- WI_ATOMIC_I32_OR16_U = ATOMIC_INSTR_MASK | 0x36,
- WI_ATOMIC_I64_OR8_U = ATOMIC_INSTR_MASK | 0x37,
- WI_ATOMIC_I64_OR16_U = ATOMIC_INSTR_MASK | 0x38,
- WI_ATOMIC_I64_OR32_U = ATOMIC_INSTR_MASK | 0x39,
-
- WI_ATOMIC_I32_XOR = ATOMIC_INSTR_MASK | 0x3a,
- WI_ATOMIC_I64_XOR = ATOMIC_INSTR_MASK | 0x3b,
- WI_ATOMIC_I32_XOR8_U = ATOMIC_INSTR_MASK | 0x3c,
- WI_ATOMIC_I32_XOR16_U = ATOMIC_INSTR_MASK | 0x3d,
- WI_ATOMIC_I64_XOR8_U = ATOMIC_INSTR_MASK | 0x3e,
- WI_ATOMIC_I64_XOR16_U = ATOMIC_INSTR_MASK | 0x3f,
- WI_ATOMIC_I64_XOR32_U = ATOMIC_INSTR_MASK | 0x40,
-
- WI_ATOMIC_I32_XCHG = ATOMIC_INSTR_MASK | 0x41,
- WI_ATOMIC_I64_XCHG = ATOMIC_INSTR_MASK | 0x42,
- WI_ATOMIC_I32_XCHG8_U = ATOMIC_INSTR_MASK | 0x43,
- WI_ATOMIC_I32_XCHG16_U = ATOMIC_INSTR_MASK | 0x44,
- WI_ATOMIC_I64_XCHG8_U = ATOMIC_INSTR_MASK | 0x45,
- WI_ATOMIC_I64_XCHG16_U = ATOMIC_INSTR_MASK | 0x46,
- WI_ATOMIC_I64_XCHG32_U = ATOMIC_INSTR_MASK | 0x47,
-
- WI_ATOMIC_I32_CMPXCHG = ATOMIC_INSTR_MASK | 0x48,
- WI_ATOMIC_I64_CMPXCHG = ATOMIC_INSTR_MASK | 0x49,
- WI_ATOMIC_I32_CMPXCHG8_U = ATOMIC_INSTR_MASK | 0x4a,
- WI_ATOMIC_I32_CMPXCHG16_U = ATOMIC_INSTR_MASK | 0x4b,
- WI_ATOMIC_I64_CMPXCHG8_U = ATOMIC_INSTR_MASK | 0x4c,
- WI_ATOMIC_I64_CMPXCHG16_U = ATOMIC_INSTR_MASK | 0x4d,
- WI_ATOMIC_I64_CMPXCHG32_U = ATOMIC_INSTR_MASK | 0x4e,
-} 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;
- OnyxToken *location;
-} 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;
- union {
- i32 idx;
- struct {
- i32 min, max;
- b32 shared;
- };
- };
- char *mod, *name;
-} WasmImport;
-
-typedef struct WasmDatum {
- u32 id;
- u32 offset_, alignment;
- u32 length;
- ptr data;
-} WasmDatum;
-
-typedef enum DatumPatchInfoKind {
- Datum_Patch_Instruction,
- Datum_Patch_Data,
- Datum_Patch_Relative,
-} DatumPatchInfoKind;
-
-//
-// This represents a pointer that should be filled in
-// later when the corresponding data element is placed.
-//
-// There are three kinds of patches:
-// - Instruction
-// - Data
-// - Relative
-//
-// In all cases, the `data_id` member is set to the id
-// of the WasmDatum entry that will be the base address,
-// and then the `offset` member will be added to that.
-//
-// In instruction patches, the `index` member is set
-// to the index of the function where the instruction should
-// be patched. The `location` member is set to the instruction
-// that needs to have its data changed.
-//
-// In data patches, the `index` member is set to the id
-// of the WasmDatum entry that needs to have a part of it
-// updated. The `location` member is the offset into the
-// data to update. It is assumed that 4 bytes will be reserved
-// to be replaced with the pointer value.
-//
-// In relative patches, `index` member is set to the id
-// of the WasmDatum entry that needs to have a part of it
-// updated. The `location` member is the offset into the
-// data to update. The difference between `Data` and `Relative`
-// is that `Relative` *adds* the base address to the current
-// value in the 4 bytes, as opposed to replacing it. As a
-// convenience, if the value is 0 (null), it will remain as
-// 0.
-//
-typedef struct DatumPatchInfo {
- DatumPatchInfoKind kind;
- u32 data_id;
- u32 offset;
- u32 location;
- u32 index;
-} DatumPatchInfo;
-
-// Context used when building a constexpr buffer
-typedef struct ConstExprContext {
- struct OnyxWasmModule *module;
- ptr data;
- u32 data_id;
-} ConstExprContext;
-
-typedef enum DeferredStmtType {
- Deferred_Stmt_Node,
- Deferred_Stmt_Code,
-} DeferredStmtType;
-
-typedef struct DeferredStmt {
- DeferredStmtType type;
- u32 depth;
- AstDefer *defer_node;
-
- union {
- AstNode *stmt;
- struct {
- WasmInstruction* instructions;
- u32 instruction_count;
- };
- };
-} DeferredStmt;
-
-typedef struct AllocatedSpace {
- u64 depth;
- AstTyped *expr;
-} AllocatedSpace;
-
-typedef struct StrLitInfo {
- u32 data_id;
- u32 len;
-} StrLitInfo;
-
-typedef struct PatchInfo {
- u32 instruction_index;
-} PatchInfo;
-
-typedef struct ForRemoveInfo {
- // These are WASM locals
- u64 iterator_remove_func;
- u64 iterator_data_ptr;
-
- i32 remove_func_type_idx;
-} ForRemoveInfo;
-
-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;
-
- i32 current_func_idx;
- 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;
- bh_arr(DatumPatchInfo) data_patches;
-
- bh_arr(ForRemoveInfo) for_remove_info;
-
- bh_arr(AstForeignBlock *) foreign_blocks;
- u32 next_foreign_block_idx;
-
- bh_arr(AstFunction *) procedures_with_tags;
-
- // 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.
- Table(i32) type_map;
-
- Table(StrLitInfo) loaded_file_info;
- 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;
- Table(WasmExport) exports;
- bh_arr(WasmGlobal) globals;
- bh_arr(WasmFunc) funcs;
- bh_arr(WasmDatum) data;
- bh_arr(i32) elems;
- bh_arr(char *) libraries;
- bh_arr(char *) library_paths;
- b32 needs_memory_section;
- u32 memory_min_size;
- u32 memory_max_size;
-
- // 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_tls_offset;
- u32 next_elem_idx;
- u32 foreign_function_count;
-
- i32 *stack_top_ptr;
- i32 *tls_size_ptr;
- i32 *heap_start_ptr;
- u64 stack_base_idx;
- CallingConvention curr_cc;
- i32 null_proc_func_idx;
-
- b32 has_stack_locals : 1;
-
-#ifdef ENABLE_DEBUG_INFO
- struct DebugContext *debug_context;
-#endif
-
-} OnyxWasmModule;
-
-typedef struct OnyxWasmLinkOptions {
- b32 stack_first;
- u32 stack_size;
- u32 stack_alignment;
-
- u32 null_reserve_size;
-
- b32 import_memory;
- char *import_memory_module_name;
- char *import_memory_import_name;
-
- b32 export_memory;
- char *export_memory_name;
-
- b32 export_func_table;
- char *export_func_table_name;
-
- u32 memory_min_size;
- u32 memory_max_size;
-} OnyxWasmLinkOptions;
-
-b32 onyx_wasm_build_link_options_from_node(OnyxWasmLinkOptions *opts, struct AstTyped *node);
-
-OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc);
-void onyx_wasm_module_link(OnyxWasmModule *module, OnyxWasmLinkOptions *options);
-void onyx_wasm_module_free(OnyxWasmModule* module);
-void onyx_wasm_module_write_to_buffer(OnyxWasmModule* module, bh_buffer* buffer);
-void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file);
-
-#ifdef ENABLE_RUN_WITH_WASMER
-void onyx_run_initialize(b32 debug_enabled);
-b32 onyx_run_wasm(bh_buffer code_buffer, int argc, char *argv[]);
-#endif
-
-#ifdef ENABLE_DEBUG_INFO
-
-typedef enum DebugOpType {
- DOT_END = 0b00000000,
- DOT_SET = 0b00000001,
- DOT_PUSHF = 0b00000010,
- DOT_POPF = 0b00000011,
- DOT_SYM = 0b00000100,
-
- DOT_INC = 0b01000000,
- DOT_DEC = 0b10000000,
- DOT_REP = 0b11000000,
-} DebugOpType;
-
-typedef struct DebugFuncContext {
- u32 func_index;
- u32 file_id;
- u32 line;
- u32 op_offset;
- u32 stack_ptr_idx;
-
- u32 name_length;
- char *name;
-} DebugFuncContext;
-
-typedef struct DebugFileInfo {
- u32 file_id;
- u32 line_count;
-} DebugFileInfo;
-
-typedef enum DebugSymLoc {
- DSL_REGISTER = 1,
- DSL_STACK = 2,
- DSL_GLOBAL = 3,
-} DebugSymLoc;
-
-typedef struct DebugSymInfo {
- u32 sym_id;
- u32 location_type;
- u64 location_num;
- char *name;
- u32 type;
-} DebugSymInfo;
-
-typedef struct DebugSymPatch {
- u32 func_idx;
- u32 sym_id;
- u64 local_idx;
-} DebugSymPatch;
-
-typedef struct DebugContext {
- bh_allocator allocator;
-
- Table(DebugFileInfo) file_info;
- u32 next_file_id;
-
- bh_arr(DebugSymInfo) sym_info;
- bh_arr(DebugSymPatch) sym_patches;
- u32 next_sym_id;
-
- bh_arr(DebugFuncContext) funcs;
- bh_buffer op_buffer;
-
- // Used during building the debug info
- OnyxToken *last_token;
- b32 last_op_was_rep : 1;
-} DebugContext;
-
-#endif
-
-#endif
--- /dev/null
+#!/bin/sh
+
+. ../settings.sh
+
+$CC -shared -fpic -w \
+ -o ../bin/onyx_runtime.so \
+ -I ../shared/include -I ../compiler/include -I ../lib/common/include \
+ ./onyx_runtime.c \
+ -lpthread
+
+echo "Installing onyx_runtime.so"
+sudo mv "../bin/onyx_runtime.so" "$CORE_DIR/lib/onyx_runtime.so"
--- /dev/null
+
+#define BH_DEFINE
+#include "bh.h"
+
+#define ONYX_LIBRARY_NAME onyx_runtime
+#define ONYX_NO_SHORT_NAMES
+#include "onyx_library.h"
+
+#ifdef _BH_LINUX
+ #include <pthread.h>
+ #include <signal.h>
+ #include <sys/wait.h>
+ #include <sys/types.h>
+ #include <dlfcn.h>
+ #include <dirent.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <netinet/in.h>
+ #include <sys/socket.h>
+ #include <sys/un.h>
+ #include <poll.h>
+#endif
+
+#include "types.h" // For POINTER_SIZE
+
+#define ONYX_FILE_ERROR_NONE 0
+#define ONYX_FILE_ERROR_NOT_FOUND 1
+#define ONYX_FILE_ERROR_EXISTS 2
+#define ONYX_FILE_ERROR_PERMISSION 3
+#define ONYX_FILE_ERROR_BAD_FILE 4
+#define ONYX_FILE_ERROR_BAD_MODE 5
+
+ONYX_DEF(__file_open_impl, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ char *path_ptr = ONYX_PTR(params->data[0].of.i32);
+ int path_len = params->data[1].of.i32;
+
+ char path[512] = {0};
+ path_len = bh_min(path_len, 511);
+ strncpy(path, path_ptr, path_len);
+ path[path_len] = 0;
+
+ int mode = params->data[2].of.i32;
+
+ bh_file_mode bh_mode;
+ switch (mode) {
+ case 1: bh_mode = BH_FILE_MODE_READ; break;
+ case 2: bh_mode = BH_FILE_MODE_WRITE; break;
+ case 3: bh_mode = BH_FILE_MODE_APPEND; break;
+ }
+
+ bh_file file;
+ bh_file_error error = bh_file_open_mode(&file, bh_mode, path);
+ if (error == BH_FILE_ERROR_INVALID) {
+ results->data[0] = WASM_I32_VAL(ONYX_FILE_ERROR_NOT_FOUND);
+ return NULL;
+ }
+
+ *(u64 *) ONYX_PTR(params->data[3].of.i32) = (u64) file.fd;
+ results->data[0] = WASM_I32_VAL(ONYX_FILE_ERROR_NONE);
+ return NULL;
+}
+
+ONYX_DEF(__file_close, (WASM_I64), (WASM_I32)) {
+ i64 fd = params->data[0].of.i64;
+
+ bh_file file = { (bh_file_descriptor) fd };
+ bh_file_error error = bh_file_close(&file);
+ if (error == BH_FILE_ERROR_INVALID) {
+ results->data[0] = WASM_I32_VAL(ONYX_FILE_ERROR_NOT_FOUND);
+ return NULL;
+ }
+
+ results->data[0] = WASM_I32_VAL(ONYX_FILE_ERROR_NONE);
+ return NULL;
+}
+
+ONYX_DEF(__file_exists, (WASM_I32, WASM_I32), (WASM_I32)) {
+ char *path_ptr = ONYX_PTR(params->data[0].of.i32);
+ int path_len = params->data[1].of.i32;
+
+ char path[512] = {0};
+ path_len = bh_min(path_len, 511);
+ strncpy(path, path_ptr, path_len);
+ path[path_len] = 0;
+
+ results->data[0] = WASM_I32_VAL(bh_file_exists(path));
+ return NULL;
+}
+
+ONYX_DEF(__file_stat, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ char *path_ptr = ONYX_PTR(params->data[0].of.i32);
+ int path_len = params->data[1].of.i32;
+
+ char path[512] = {0};
+ path_len = bh_min(path_len, 511);
+ strncpy(path, path_ptr, path_len);
+ path[path_len] = 0;
+
+ results->data[0] = WASM_I32_VAL(bh_file_stat(path, ONYX_PTR(params->data[2].of.i32)));
+ return NULL;
+}
+
+ONYX_DEF(__file_remove, (WASM_I32, WASM_I32), (WASM_I32)) {
+ char *path_ptr = ONYX_PTR(params->data[0].of.i32);
+ int path_len = params->data[1].of.i32;
+
+ char path[512] = {0};
+ path_len = bh_min(path_len, 511);
+ strncpy(path, path_ptr, path_len);
+ path[path_len] = 0;
+
+ results->data[0] = WASM_I32_VAL(bh_file_remove(path));
+ return NULL;
+}
+
+ONYX_DEF(__file_seek, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
+ i64 fd = params->data[0].of.i64;
+ i32 offset = params->data[1].of.i32;
+ i32 whence = params->data[2].of.i32;
+
+ bh_file file = { (bh_file_descriptor) fd };
+ bh_file_whence bh_whence;
+ switch (whence) {
+ case 0: bh_whence = BH_FILE_WHENCE_BEGIN; break;
+ case 1: bh_whence = BH_FILE_WHENCE_CURRENT; break;
+ case 2: bh_whence = BH_FILE_WHENCE_END; break;
+ }
+
+ i64 new_offset = bh_file_seek(&file, offset, whence);
+ results->data[0] = WASM_I32_VAL((i32) new_offset);
+ return NULL;
+}
+
+ONYX_DEF(__file_tell, (WASM_I64), (WASM_I32)) {
+ i64 fd = params->data[0].of.i64;
+ bh_file file = { (bh_file_descriptor) fd };
+ results->data[0] = WASM_I32_VAL(bh_file_tell(&file));
+ return NULL;
+}
+
+ONYX_DEF(__file_read, (WASM_I64, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ i64 fd = params->data[0].of.i64;
+ bh_file file = { (bh_file_descriptor) fd };
+
+ i32 curr_pos = bh_file_tell(&file);
+ b32 success = bh_file_read_at(&file,
+ bh_file_tell(&file),
+ ONYX_PTR(params->data[1].of.i32),
+ params->data[2].of.i32,
+ (i64 *) ONYX_PTR(params->data[3].of.i32));
+
+ bh_file_seek_to(&file, curr_pos + *(i32 *) ONYX_PTR(params->data[3].of.i32));
+
+ results->data[0] = WASM_I32_VAL(0);
+ if (!success) results->data[0] = WASM_I32_VAL(2);
+ return NULL;
+}
+
+ONYX_DEF(__file_write, (WASM_I64, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ i64 fd = params->data[0].of.i64;
+ bh_file file = { (bh_file_descriptor) fd };
+
+ i32 curr_pos = bh_file_tell(&file);
+ b32 success = bh_file_write_at(&file,
+ bh_file_tell(&file),
+ ONYX_PTR(params->data[1].of.i32),
+ params->data[2].of.i32,
+ (i64 *) ONYX_PTR(params->data[3].of.i32));
+
+ bh_file_seek_to(&file, curr_pos + *(i32 *) ONYX_PTR(params->data[3].of.i32));
+
+ results->data[0] = WASM_I32_VAL(0);
+ if (!success) results->data[0] = WASM_I32_VAL(2);
+ return NULL;
+}
+
+ONYX_DEF(__file_flush, (WASM_I64), (WASM_I32)) {
+ i64 fd = params->data[0].of.i64;
+ bh_file file = { (bh_file_descriptor) fd };
+ bh_file_flush(&file);
+ results->data[0] = WASM_I32_VAL(0);
+ return NULL;
+}
+
+ONYX_DEF(__file_size, (WASM_I64), (WASM_I32)) {
+ i64 fd = params->data[0].of.i64;
+ bh_file file = { (bh_file_descriptor) fd };
+ results->data[0] = WASM_I32_VAL(bh_file_size(&file));
+ return NULL;
+}
+
+ONYX_DEF(__file_get_standard, (WASM_I32, WASM_I32), (WASM_I32)) {
+ bh_file_standard standard = (bh_file_standard) params->data[0].of.i32;
+
+ bh_file file;
+ bh_file_error error = bh_file_get_standard(&file, standard);
+ if (error == BH_FILE_ERROR_NONE) {
+ *(u64 *) ONYX_PTR(params->data[1].of.i32) = (u64) file.fd;
+ }
+
+ results->data[0] = WASM_I32_VAL(error == BH_FILE_ERROR_NONE);
+ return NULL;
+}
+
+ONYX_DEF(__file_rename, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ char *old_path_ptr = ONYX_PTR(params->data[0].of.i32);
+ int old_path_len = params->data[1].of.i32;
+
+ char old_path[512] = {0};
+ old_path_len = bh_min(old_path_len, 511);
+ strncpy(old_path, old_path_ptr, old_path_len);
+ old_path[old_path_len] = 0;
+
+ char *new_path_ptr = ONYX_PTR(params->data[2].of.i32);
+ int new_path_len = params->data[3].of.i32;
+
+ char new_path[512] = {0};
+ new_path_len = bh_min(new_path_len, 511);
+ strncpy(new_path, new_path_ptr, new_path_len);
+ new_path[new_path_len] = 0;
+
+#ifdef _BH_WINDOWS
+ results->data[0] = WASM_I32_VAL(MoveFileA(old_path, new_path));
+ return NULL;
+#endif
+
+#ifdef _BH_LINUX
+ results->data[0] = WASM_I32_VAL(rename(old_path, new_path) == 0);
+ return NULL;
+#endif
+}
+
+//
+// Directories
+//
+
+ONYX_DEF(__dir_open, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ char *path_ptr = ONYX_PTR(params->data[0].of.i32);
+ int path_len = params->data[1].of.i32;
+
+ char path[512] = {0};
+ path_len = bh_min(path_len, 511);
+ strncpy(path, path_ptr, path_len);
+ path[path_len] = 0;
+
+#ifdef _BH_WINDOWS
+ for (int i=0; i<path_len; i++) if (path[i] == '/') path[i] = '\\';
+ strncat(path, "\\*.*", 511);
+
+ Windows_Directory_Opened* dir = malloc(sizeof(Windows_Directory_Opened));
+ dir->hndl = FindFirstFileA(path, &dir->found_file);
+ if (dir->hndl == INVALID_HANDLE_VALUE) {
+ results->data[0] = WASM_I32_VAL(0);
+ return NULL;
+ }
+
+ *(u64 *) ONYX_PTR(params->data[2].of.i32) = (u64) dir;
+
+ results->data[0] = WASM_I32_VAL(1);
+ return NULL;
+#endif
+
+#ifdef _BH_LINUX
+ DIR* dir = opendir(path);
+ *(u64 *) ONYX_PTR(params->data[2].of.i32) = (u64) dir;
+ results->data[0] = WASM_I32_VAL(dir != NULL);
+ return NULL;
+#endif
+}
+
+// (DIR*, PTR<DIRENT>) -> BOOL
+ONYX_DEF(__dir_read, (WASM_I64, WASM_I32), (WASM_I32)) {
+#ifdef _BH_WINDOWS
+ Windows_Directory_Opened* dir = (Windows_Directory_Opened *) params->data[0].of.i64;
+ if (dir == NULL) {
+ results->data[0] = WASM_I32_VAL(0);
+ return NULL;
+ }
+
+ do {
+ BOOL success = FindNextFileA(dir->hndl, &dir->found_file);
+ if (!success) {
+ results->data[0] = WASM_I32_VAL(0);
+ return NULL;
+ }
+ } while (!strcmp(dir->found_file.cFileName, ".") || !strcmp(dir->found_file.cFileName, ".."));
+
+ u32 out = params->data[1].of.i32;
+ assert(out != 0);
+
+ *(u32 *) ONYX_PTR(out + 0) = (dir->found_file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 3 : 4;
+ *(u32 *) ONYX_PTR(out + 4) = 0;
+ *(u32 *) ONYX_PTR(out + 8) = strlen(dir->found_file.cFileName);
+ strncpy(ONYX_PTR(out + 12), dir->found_file.cFileName, 256);
+
+ results->data[0] = WASM_I32_VAL(1);
+ return NULL;
+#endif
+
+#ifdef _BH_LINUX
+ DIR* dir = (DIR *) params->data[0].of.i64;
+ if (dir == NULL) {
+ results->data[0] = WASM_I32_VAL(0);
+ return NULL;
+ }
+
+ struct dirent *ent;
+ while (1) {
+ ent = readdir(dir);
+ if (ent == NULL) {
+ results->data[0] = WASM_I32_VAL(0);
+ return NULL;
+ }
+
+ // Skip the current directory and parent directory
+ if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) break;
+ }
+
+ u32 type = 0;
+ switch (ent->d_type) {
+ case DT_UNKNOWN: break;
+ case DT_BLK: type = 1; break;
+ case DT_CHR: type = 2; break;
+ case DT_DIR: type = 3; break;
+ case DT_LNK: type = 5; break;
+ case DT_REG: type = 4; break;
+ default: type = 6; break;
+ }
+
+ u32 out = params->data[1].of.i32;
+ assert(out != 0);
+
+ *(u32 *) ONYX_PTR(out + 0) = type;
+ *(u32 *) ONYX_PTR(out + 4) = (u32) ent->d_ino;
+ *(u32 *) ONYX_PTR(out + 8) = strlen(ent->d_name);
+ strncpy(ONYX_PTR(out + 12), ent->d_name, 256);
+
+ results->data[0] = WASM_I32_VAL(1);
+ return NULL;
+#endif
+}
+
+ONYX_DEF(__dir_close, (WASM_I64), ()) {
+#ifdef _BH_WINDOWS
+ Windows_Directory_Opened* dir = (Windows_Directory_Opened *) params->data[0].of.i64;
+
+ FindClose(dir->hndl);
+ free(dir);
+
+ return NULL;
+#endif
+
+#ifdef _BH_LINUX
+ DIR* dir = (DIR *) params->data[0].of.i64;
+ if (dir == NULL) return NULL;
+
+ closedir(dir);
+#endif
+ return NULL;
+}
+
+ONYX_DEF(__dir_create, (WASM_I32, WASM_I32), (WASM_I32)) {
+ char *path_ptr = ONYX_PTR(params->data[0].of.i32);
+ int path_len = params->data[1].of.i32;
+
+ char path[512] = {0};
+ path_len = bh_min(path_len, 511);
+ strncpy(path, path_ptr, path_len);
+ path[path_len] = 0;
+
+#ifdef _BH_WINDOWS
+ results->data[0] = WASM_I32_VAL(CreateDirectoryA(path));
+ return NULL;
+#endif
+
+#ifdef _BH_LINUX
+ results->data[0] = WASM_I32_VAL(mkdir(path, 0777) == 0);
+ return NULL;
+#endif
+}
+
+ONYX_DEF(__dir_remove, (WASM_I32, WASM_I32), (WASM_I32)) {
+ char *path_ptr = ONYX_PTR(params->data[0].of.i32);
+ int path_len = params->data[1].of.i32;
+
+ char path[512] = {0};
+ path_len = bh_min(path_len, 511);
+ strncpy(path, path_ptr, path_len);
+ path[path_len] = 0;
+
+#ifdef _BH_WINDOWS
+ results->data[0] = WASM_I32_VAL(RemoveDirectoryA(path));
+ return NULL;
+#endif
+
+#ifdef _BH_LINUX
+ results->data[0] = WASM_I32_VAL(rmdir(path) == 0);
+ return NULL;
+#endif
+}
+
+//
+// THREADS
+//
+typedef struct OnyxThread {
+ i32 id;
+ i32 tls_base;
+ i32 funcidx;
+ i32 dataptr;
+ wasm_instance_t* instance;
+
+ #ifdef _BH_LINUX
+ pthread_t thread;
+ #endif
+
+ #ifdef _BH_WINDOWS
+ HANDLE thread_handle;
+ i32 thread_id;
+ #endif
+} OnyxThread;
+
+static bh_arr(OnyxThread) threads = NULL;
+
+#ifdef _BH_LINUX
+static void *onyx_run_thread(void *data) {
+#endif
+#ifdef _BH_WINDOWS
+static i32 onyx_run_thread(void *data) {
+#endif
+ OnyxThread *thread = (OnyxThread *) data;
+
+ wasm_store_t *wasm_store = runtime->wasm_store_new(runtime->wasm_engine);
+
+ wasm_trap_t* traps = NULL;
+ thread->instance = runtime->wasm_instance_new(wasm_store, runtime->wasm_module, &runtime->wasm_imports, &traps);
+
+ wasm_extern_t* start_extern = runtime->wasm_extern_lookup_by_name(runtime->wasm_module, thread->instance, "_thread_start");
+ wasm_func_t* start_func = runtime->wasm_extern_as_func(start_extern);
+
+ wasm_extern_t* exit_extern = runtime->wasm_extern_lookup_by_name(runtime->wasm_module, thread->instance, "_thread_exit");
+ wasm_func_t* exit_func = runtime->wasm_extern_as_func(exit_extern);
+
+ wasm_trap_t* trap=NULL;
+
+ // NOTE: This is cached in a local variable because there is a tiny chance that if a lot of threads are created
+ // then the backing array for the thread handles will move and thread* we have well not be valid. I'm betting on this
+ // not happening before now in the function; however, it *could* happen before we call thread_exit. This would be bad
+ // because of the normal reasons why accessing memory that you don't own any more is bad.
+ i32 thread_id = thread->id;
+
+ { // Call the _thread_start procedure
+ wasm_val_t args[] = { WASM_I32_VAL(thread_id), WASM_I32_VAL(thread->tls_base), WASM_I32_VAL(thread->funcidx), WASM_I32_VAL(thread->dataptr) };
+ wasm_val_vec_t results = { 0, 0 };
+ wasm_val_vec_t args_array = WASM_ARRAY_VEC(args);
+
+ trap = runtime->wasm_func_call(start_func, &args_array, &results);
+ if (trap != NULL) {
+ bh_printf("THREAD: %d\n", thread_id);
+ runtime->onyx_print_trap(trap);
+ }
+ }
+
+ { // Call the _thread_exit procedure
+ wasm_val_t args[] = { WASM_I32_VAL(thread_id) };
+ wasm_val_vec_t results = { 0, 0 };
+ wasm_val_vec_t args_array = WASM_ARRAY_VEC(args);
+
+ trap = runtime->wasm_func_call(exit_func, &args_array, &results);
+ }
+
+ runtime->wasm_store_delete(wasm_store);
+
+ return 0;
+}
+
+ONYX_DEF(__spawn_thread, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ if (threads == NULL) bh_arr_new(bh_heap_allocator(), threads, 128);
+ bh_arr_insert_end(threads, 1);
+ OnyxThread *thread = &bh_arr_last(threads);
+
+ thread->id = params->data[0].of.i32;
+ thread->tls_base = params->data[1].of.i32;
+ thread->funcidx = params->data[2].of.i32;
+ thread->dataptr = params->data[3].of.i32;
+
+ #ifdef _BH_LINUX
+ pthread_create(&thread->thread, NULL, onyx_run_thread, thread);
+ #endif
+
+ #ifdef _BH_WINDOWS
+ // thread->thread_handle = CreateThread(NULL, 0, onyx_run_thread, thread, 0, &thread->thread_id);
+ thread->thread_handle = (HANDLE) _beginthreadex(NULL, 0, onyx_run_thread, thread, 0, &thread->thread_id);
+ #endif
+
+ results->data[0] = WASM_I32_VAL(1);
+ return NULL;
+}
+
+ONYX_DEF(__kill_thread, (WASM_I32), (WASM_I32)) {
+ i32 thread_id = params->data[0].of.i32;
+
+ i32 i = 0;
+ bh_arr_each(OnyxThread, thread, threads) {
+ if (thread->id == thread_id) {
+ #ifdef _BH_LINUX
+ // This leads to some weirdness and bugs...
+ //
+ pthread_kill(thread->thread, SIGKILL);
+ #endif
+
+ #ifdef _BH_WINDOWS
+ TerminateThread(thread->thread_handle, 0);
+ CloseHandle(thread->thread_handle);
+ #endif
+
+ bh_arr_deleten(threads, i, 1);
+ results->data[0] = WASM_I32_VAL(1);
+ return NULL;
+ }
+
+ i++;
+ }
+
+ results->data[0] = WASM_I32_VAL(0);
+ return NULL;
+}
+
+
+
+
+//
+// PROCESS
+//
+#define ONYX_PROCESS_MAGIC_NUMBER 0xdeadfadebabecafe
+typedef struct OnyxProcess {
+ u64 magic_number;
+
+#ifdef _BH_LINUX
+ // Pipes
+ i32 proc_to_host[2];
+ i32 host_to_proc[2];
+
+ pid_t pid;
+#endif
+
+#ifdef _BH_WINDOWS
+ HANDLE proc_to_host_read;
+ HANDLE proc_to_host_write;
+ HANDLE host_to_proc_read;
+ HANDLE host_to_proc_write;
+
+ PROCESS_INFORMATION proc_info;
+#endif
+} OnyxProcess;
+
+ONYX_DEF(__process_spawn, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I64)) {
+ char* process_str = ONYX_PTR(params->data[0].of.i32);
+ i32 process_len = params->data[1].of.i32;
+ i32 args_ptr = params->data[2].of.i32;
+ i32 args_len = params->data[3].of.i32;
+ b32 blocking_io = !params->data[4].of.i32;
+ char *cwd_str = ONYX_PTR(params->data[5].of.i32);
+ i32 cwd_len = params->data[6].of.i32;
+
+ char process_path[1024];
+ process_len = bh_min(1023, process_len);
+ memcpy(process_path, process_str, process_len);
+ process_path[process_len] = '\0';
+
+ char starting_dir[1024];
+ if (cwd_len > 0) {
+ cwd_len = bh_min(1023, cwd_len);
+ memcpy(starting_dir, cwd_str, cwd_len);
+ starting_dir[cwd_len] = '\0';
+ }
+
+ OnyxProcess *process = malloc(sizeof(OnyxProcess));
+ memset(process, 0, sizeof(*process));
+ process->magic_number = ONYX_PROCESS_MAGIC_NUMBER;
+
+ #ifdef _BH_LINUX
+ // :Security - alloca a user controlled string.
+ char **process_args = alloca(sizeof(char *) * (args_len + 2));
+ byte_t* array_loc = ONYX_PTR(args_ptr);
+ fori (i, 0, args_len) {
+ char *arg_str = ONYX_PTR(*(i32 *) (array_loc + i * 2 * POINTER_SIZE));
+ i32 arg_len = *(i32 *) (array_loc + i * 2 * POINTER_SIZE + POINTER_SIZE);
+
+ char *arg = alloca(sizeof(char) * (arg_len + 1));
+ memcpy(arg, arg_str, arg_len);
+ arg[arg_len] = '\0';
+ process_args[i + 1] = arg;
+ }
+ process_args[0] = process_path;
+ process_args[args_len + 1] = NULL;
+
+ if (pipe(process->proc_to_host) || pipe(process->host_to_proc)) {
+ wasm_val_init_ptr(&results->data[0], NULL); // Failed to run
+ return NULL;
+ }
+
+ pid_t pid;
+ switch (pid = fork()) {
+ case -1: // Bad fork
+ wasm_val_init_ptr(&results->data[0], NULL); // Failed to run
+
+ close(process->proc_to_host[0]);
+ close(process->proc_to_host[1]);
+ close(process->host_to_proc[0]);
+ close(process->host_to_proc[1]);
+ break;
+
+ case 0: // Child process
+ close(process->proc_to_host[0]);
+ close(process->host_to_proc[1]);
+ dup2(process->host_to_proc[0], 0); // Map the output to the pipe
+ dup2(process->proc_to_host[1], 1); // Map the output to the pipe
+ dup2(process->proc_to_host[1], 2); // Stderr to stdout
+
+ if (!blocking_io) {
+ fcntl(0, F_SETFL, O_NONBLOCK);
+ fcntl(1, F_SETFL, O_NONBLOCK);
+ }
+
+ if (cwd_len > 0) {
+ chdir(starting_dir); // Switch current working directory.
+ }
+
+ execvp(process_path, process_args);
+ exit(-1);
+ break;
+
+ default: {
+ process->pid = pid;
+ close(process->host_to_proc[0]);
+ close(process->proc_to_host[1]);
+
+ wasm_val_init_ptr(&results->data[0], process);
+ break;
+ }
+ }
+
+ #endif
+
+ #ifdef _BH_WINDOWS
+ // CLEANUP CLEANUP CLEANUP: This is so freaking bad...
+ char cmdLine[2048];
+ memset(cmdLine, 0, 2048);
+ strncat(cmdLine, "\"", 2047);
+ strncat(cmdLine, process_path, 2047);
+ strncat(cmdLine, "\"", 2047);
+
+ byte_t* array_loc = ONYX_PTR(args_ptr);
+ fori (i, 0, args_len) {
+ char *arg_str = ONYX_PTR(*(i32 *) (array_loc + i * 2 * POINTER_SIZE));
+ i32 arg_len = *(i32 *) (array_loc + i * 2 * POINTER_SIZE + 4);
+
+ strncat(cmdLine, " \"", 2047);
+ strncat(cmdLine, arg_str, arg_len);
+ strncat(cmdLine, "\"", 2047);
+ }
+
+ STARTUPINFOA startup;
+ memset(&startup, 0, sizeof startup);
+ startup.cb = sizeof(startup);
+
+ SECURITY_ATTRIBUTES saAttr;
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.lpSecurityDescriptor = NULL;
+ saAttr.bInheritHandle = 1;
+
+ i32 success = 1;
+ success = success && CreatePipe(&process->host_to_proc_read, &process->host_to_proc_write, &saAttr, 4096);
+ success = success && CreatePipe(&process->proc_to_host_read, &process->proc_to_host_write, &saAttr, 4096);
+ if (!success) {
+ // printf("FAILED TO CREATE PIPES: %d\n", GetLastError());
+ wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
+ return NULL;
+ }
+
+ success = SetHandleInformation(process->proc_to_host_read, 1 /* HANDLE_FLAG_INHERIT */, 0);
+ success = success && SetHandleInformation(process->host_to_proc_write, 1 /* HANDLE_FLAG_INHERIT */, 0);
+ if (!success) {
+ // printf("FAILED TO CONFIGURE PIPES: %d\n", GetLastError());
+ wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
+ return NULL;
+ }
+
+ startup.hStdInput = process->host_to_proc_read;
+ startup.hStdOutput = process->proc_to_host_write;
+ startup.hStdError = process->proc_to_host_write;
+
+ startup.dwFlags |= STARTF_USESTDHANDLES;
+
+ memset(&process->proc_info, 0, sizeof process->proc_info);
+
+ char *working_dir = NULL;
+ if (cwd_len > 0) {
+ working_dir = starting_dir;
+ }
+
+ success = CreateProcessA(process_path, cmdLine, &saAttr, &saAttr, 1, 0, NULL, working_dir, &startup, &process->proc_info);
+ if (!success) {
+ printf("FAILED TO CREATE PROCESS: %d\n", GetLastError());
+ wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
+ return NULL;
+ }
+
+ CloseHandle(process->proc_to_host_write);
+ CloseHandle(process->host_to_proc_read);
+ wasm_val_init_ptr(&results->data[0], process);
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__process_read, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
+ OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
+ if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
+ results->data[0] = WASM_I32_VAL(0);
+ return NULL;
+ }
+
+ i32 output_ptr = params->data[1].of.i32;
+ i32 output_len = params->data[2].of.i32;
+ u8 *buffer = ONYX_PTR(output_ptr);
+
+ i32 bytes_read;
+ #ifdef _BH_LINUX
+ bytes_read = read(process->proc_to_host[0], buffer, output_len);
+ if (bytes_read < 0) {
+ switch (errno) {
+ case EAGAIN: bytes_read = 0; break;
+ case EBADF: bytes_read = -1; break;
+ default: bytes_read = -2; break;
+ }
+ }
+ #endif
+
+ #ifdef _BH_WINDOWS
+ i32 success = ReadFile(process->proc_to_host_read, buffer, output_len, &bytes_read, NULL);
+ if (!success) {
+ bytes_read = -1;
+ }
+ #endif
+
+ results->data[0] = WASM_I32_VAL(bytes_read);
+ return NULL;
+}
+
+ONYX_DEF(__process_write, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
+ OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
+ if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
+ results->data[0] = WASM_I32_VAL(0);
+ return NULL;
+ }
+
+ i32 input_ptr = params->data[1].of.i32;
+ i32 input_len = params->data[2].of.i32;
+ u8 *buffer = ONYX_PTR(input_ptr);
+
+ i32 bytes_written;
+ #ifdef _BH_LINUX
+ bytes_written = write(process->host_to_proc[1], buffer, input_len);
+ bytes_written = bh_max(bytes_written, 0); // Silently consume errors
+ #endif
+
+ #ifdef _BH_WINDOWS
+ i32 success = WriteFile(process->host_to_proc_write, buffer, input_len, &bytes_written, NULL);
+ if (!success) bytes_written = 0;
+ #endif
+
+ results->data[0] = WASM_I32_VAL(bytes_written);
+ return NULL;
+}
+
+ONYX_DEF(__process_kill, (WASM_I64), (WASM_I32)) {
+ OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
+ if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
+ results->data[0] = WASM_I32_VAL(0);
+ return NULL;
+ }
+
+ #ifdef _BH_LINUX
+ i32 failed = kill(process->pid, SIGKILL);
+ results->data[0] = WASM_I32_VAL(!failed);
+ #endif
+
+ #ifdef _BH_WINDOWS
+ i32 success = TerminateProcess(process->proc_info.hProcess, 1);
+ results->data[0] = WASM_I32_VAL(success ? 1 : 0);
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__process_wait, (WASM_I64), (WASM_I32)) {
+ OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
+ if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
+ results->data[0] = WASM_I32_VAL(1);
+ return NULL;
+ }
+
+ #ifdef _BH_LINUX
+ i32 status;
+ waitpid(process->pid, &status, 0);
+
+ i32 exit_code = WEXITSTATUS(status);
+ results->data[0] = WASM_I32_VAL(exit_code != 0 ? 2 : 0);
+ #endif
+
+ #ifdef _BH_WINDOWS
+ DWORD exitCode;
+ while (1) {
+ if (!WaitForSingleObject(process->proc_info.hProcess, INFINITE)) {
+ // HACK HACK HACK
+ DWORD error = GetLastError();
+ if (error != 109 && error != 6) {
+ // printf("ERROR IN WAIT FOR SINGLE: %d\n", error);
+ results->data[0] = WASM_I32_VAL(1);
+ return NULL;
+ }
+ }
+
+ if (!GetExitCodeProcess(process->proc_info.hProcess, &exitCode)) {
+ // HACK HACK HACK
+ // Apparently, I'm doing something wrong (maybe?) where the process handle becomes
+ // invalid and causes error 6 "invalid handle". So I think I can safely assume that
+ // if that is the case, then the process exited? probably successfuly? hopefully?
+ // Honestly I don't know and I can't find any documentation describing when a process
+ // handle goes invalid, other than after you close it explicitly. But in the run_tests
+ // script, I'm not calling either process_kill or process_destroy, which are the only
+ // other functions that close the process handle. So I'm left in the dark as to why this
+ // is happening, but oh well. This works for now.
+ // - brendanfh 2021/12/03
+ if (GetLastError() == 6) {
+ exitCode = 0;
+ break;
+ }
+
+ results->data[0] = WASM_I32_VAL(3);
+ return NULL;
+ }
+
+ // 259 is STILL_ACTIVE (aka STATUS_PENDING), which means that the process has not yet exited
+ if (exitCode != 259) break;
+ }
+
+ results->data[0] = WASM_I32_VAL(exitCode != 0 ? 2 : 0);
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__process_destroy, (WASM_I64), ()) {
+ OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
+ if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
+ return NULL;
+ }
+
+ #ifdef _BH_LINUX
+ close(process->proc_to_host[0]);
+ close(process->host_to_proc[1]);
+ #endif
+
+ #ifdef _BH_WINDOWS
+ if (!CloseHandle(process->proc_info.hThread)
+ || !CloseHandle(process->proc_info.hProcess)) {
+ // printf("ERROR CLOSING HANDLES: %d\n", GetLastError());
+ }
+ #endif
+
+ free(process);
+
+ return NULL;
+}
+
+ONYX_DEF(__args_get, (WASM_I32, WASM_I32), ()) {
+ if (runtime->argc == 0 || runtime->argv == NULL) {
+ return NULL;
+ }
+
+ i32 buffer_base = params->data[1].of.i32;
+
+ for (int i=0; i<runtime->argc; i++) {
+ // Should this be strncpy? What would the length be?
+ strcpy(ONYX_PTR(buffer_base), runtime->argv[i]);
+ *(i32 *) ONYX_PTR(params->data[0].of.i32 + i * 4) = buffer_base;
+ buffer_base += strlen(runtime->argv[i]) + 1;
+ }
+
+ return NULL;
+}
+
+ONYX_DEF(__args_sizes_get, (WASM_I32, WASM_I32), ()) {
+ *(i32 *) ONYX_PTR(params->data[0].of.i32) = runtime->argc;
+
+ i32 argv_size = 0;
+ for (int i=0; i<runtime->argc; i++) {
+ argv_size += strlen(runtime->argv[i]) + 1;
+ }
+
+ *(i32 *) ONYX_PTR(params->data[1].of.i32) = argv_size;
+ return NULL;
+}
+
+ONYX_DEF(__exit, (WASM_I32), ()) {
+ exit(params->data[0].of.i32);
+ return NULL;
+}
+
+ONYX_DEF(__sleep, (WASM_I32), ()) {
+ #ifdef _BH_LINUX
+ usleep(params->data[0].of.i32 * 1000);
+ #endif
+
+ #ifdef _BH_WINDOWS
+ Sleep(params->data[0].of.i32);
+ #endif
+ return NULL;
+}
+
+ONYX_DEF(__time, (), (WASM_I64)) {
+ results->data[0] = WASM_I64_VAL(bh_time_curr());
+ return NULL;
+}
+
+
+
+
+//
+// Dates and Times
+//
+ONYX_DEF(__time_localtime, (WASM_I64, WASM_I32), ()) {
+ u64 t = params->data[0].of.i64;
+ *(struct tm *) ONYX_PTR(params->data[1].of.i32) = *localtime(&t);
+ return NULL;
+}
+
+ONYX_DEF(__time_gmtime, (WASM_I64, WASM_I32), ()) {
+ u64 t = params->data[0].of.i64;
+ *(struct tm *) ONYX_PTR(params->data[1].of.i32) = *gmtime(&t);
+ return NULL;
+}
+
+ONYX_DEF(__time_strftime, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ u32 len = strftime(ONYX_PTR(params->data[0].of.i32), params->data[1].of.i32, ONYX_PTR(params->data[2].of.i32), ONYX_PTR(params->data[3].of.i32));
+ results->data[0] = WASM_I32_VAL(len);
+ return NULL;
+}
+
+ONYX_DEF(__time_strptime, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ #if defined(_BH_LINUX)
+ char *rem = strptime(ONYX_PTR(params->data[0].of.i32), params->data[1].of.i32, ONYX_PTR(params->data[2].of.i32), ONYX_PTR(params->data[3].of.i32));
+ results->data[0] = WASM_I32_VAL(rem != NULL);
+ #else
+ results->data[0] = WASM_I32_VAL(0);
+ #endif
+ return NULL;
+}
+
+
+
+//
+// Networking
+//
+struct onyx_socket_addr {
+ unsigned short family;
+ unsigned short port;
+ unsigned int addr;
+};
+
+static inline int onyx_socket_domain(int i) {
+ #if defined(_BH_LINUX)
+ switch (i) { // :EnumDependent
+ case 0: return AF_UNIX;
+ case 1: return AF_INET;
+ case 2: return AF_INET6;
+ default: return -1;
+ }
+ #elif defined(_BH_WINDOWS)
+ return -1;
+ #endif
+}
+
+static inline int onyx_socket_protocol(int i) {
+ #if defined(_BH_LINUX)
+ switch (i) { // :EnumDependent
+ case 0: return SOCK_STREAM;
+ case 1: return SOCK_DGRAM;
+ default: return -1;
+ }
+ #elif defined(_BH_WINDOWS)
+ return -1;
+ #endif
+}
+
+ONYX_DEF(__net_create_socket, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+
+ #ifdef _BH_LINUX
+ int domain = onyx_socket_domain(params->data[1].of.i32);
+ if (domain == -1) goto bad_settings;
+
+ int type = onyx_socket_protocol(params->data[2].of.i32);
+ if (type == -1) goto bad_settings;
+
+ *((int *) ONYX_PTR(params->data[0].of.i32)) = socket(domain, type, 0);
+
+ results->data[0] = WASM_I32_VAL(0);
+ return NULL;
+ #endif
+
+ #ifdef _BH_WINDOWS
+ #endif
+
+bad_settings:
+ results->data[0] = WASM_I32_VAL(1); // :EnumDependent
+ return NULL;
+}
+
+ONYX_DEF(__net_close_socket, (WASM_I32), ()) {
+ #ifdef _BH_LINUX
+ shutdown(params->data[0].of.i32, SHUT_RDWR);
+ close(params->data[0].of.i32);
+ #endif
+
+ #ifdef _BH_WINDOWS
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__net_setting, (WASM_I32, WASM_I32, WASM_I32), ()) {
+ #ifdef _BH_LINUX
+ switch (params->data[1].of.i32) {
+ case 1: { // :EnumDependent Non-Blocking
+ int s = params->data[0].of.i32;
+ int flags = fcntl(s, F_GETFL, 0);
+ if (params->data[2].of.i32) {
+ flags |= O_NONBLOCK;
+ } else {
+ flags &= ~O_NONBLOCK;
+ }
+
+ fcntl(s, F_SETFL, flags);
+ break;
+ }
+
+ case 2: { // :EnumDependent Broadcast
+ int s = params->data[0].of.i32;
+ setsockopt(s, SOL_SOCKET, SO_BROADCAST, (void *) ¶ms->data[2].of.i32, sizeof(int));
+ break;
+ }
+ }
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__net_bind, (WASM_I32, WASM_I32), (WASM_I32)) {
+
+ #ifdef _BH_LINUX
+ int res = -1;
+
+ struct onyx_socket_addr *oaddr = (void *) ONYX_PTR(params->data[1].of.i32);
+ int family = onyx_socket_domain(oaddr->family);
+ int port = oaddr->port;
+
+ switch (family) {
+ case AF_INET: {
+ struct sockaddr_in bind_addr;
+ memset(&bind_addr, 0, sizeof(bind_addr));
+
+ bind_addr.sin_family = AF_INET;
+ bind_addr.sin_addr.s_addr = htonl(oaddr->addr);
+ bind_addr.sin_port = htons(port);
+
+ res = bind(params->data[0].of.i32, &bind_addr, sizeof(bind_addr));
+ break;
+ }
+
+ case AF_INET6: {
+ struct sockaddr_in6 bind_addr;
+ memset(&bind_addr, 0, sizeof(bind_addr));
+
+ bind_addr.sin6_family = AF_INET6;
+ memcpy(&bind_addr.sin6_addr.s6_addr, (void *) &oaddr->addr, 16);
+ bind_addr.sin6_port = htons(port);
+
+ res = bind(params->data[0].of.i32, &bind_addr, sizeof(bind_addr));
+ break;
+ }
+
+ case AF_UNIX: {
+ struct sockaddr_un bind_addr;
+ memset(&bind_addr, 0, sizeof(bind_addr));
+
+ bind_addr.sun_family = AF_UNIX;
+ strncpy(&bind_addr.sun_path, (char *) &oaddr->addr, 108);
+
+ res = bind(params->data[0].of.i32, &bind_addr, sizeof(bind_addr));
+ break;
+ }
+ }
+
+ results->data[0] = WASM_I32_VAL(res >= 0);
+ #endif
+
+ #ifdef _BH_WINDOWS
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__net_listen, (WASM_I32, WASM_I32), ()) {
+ #ifdef _BH_LINUX
+ listen(params->data[0].of.i32, params->data[1].of.i32);
+ #endif
+
+ #ifdef _BH_WINDOWS
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__net_accept, (WASM_I32, WASM_I32), (WASM_I32)) {
+ #ifdef _BH_LINUX
+ struct sockaddr_in client_addr;
+ int client_len = sizeof(client_addr);
+ memset(&client_addr, 0, client_len);
+
+ int client_socket = accept(params->data[0].of.i32, &client_addr, &client_len);
+
+ struct onyx_socket_addr* out_addr = (struct onyx_socket_addr *) ONYX_PTR(params->data[1].of.i32);
+ out_addr->family = client_addr.sin_family;
+ out_addr->port = ntohs(client_addr.sin_port);
+ out_addr->addr = ntohl(client_addr.sin_addr.s_addr);
+
+ results->data[0] = WASM_I32_VAL(client_socket);
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__net_connect_unix, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ #ifdef _BH_LINUX
+ int hostlen = params->data[2].of.i32;
+ char *hostname = alloca(hostlen + 1);
+ memcpy(hostname, ONYX_PTR(params->data[1].of.i32), hostlen);
+ hostname[hostlen] = '\0';
+
+ struct sockaddr_un server_addr;
+ memset(&server_addr, 0, sizeof(server_addr));
+
+ server_addr.sun_family = AF_UNIX; // See comment above
+ memcpy((char *)&server_addr.sun_path, hostname, hostlen);
+
+ int result = connect(params->data[0].of.i32, &server_addr, sizeof(server_addr));
+ if (result == 0) results->data[0] = WASM_I32_VAL(0);
+ else results->data[0] = WASM_I32_VAL(3); // :EnumDependent
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__net_connect_ipv4, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ #ifdef _BH_LINUX
+ int hostlen = params->data[2].of.i32;
+ char *hostname = alloca(hostlen + 1);
+ memcpy(hostname, ONYX_PTR(params->data[1].of.i32), hostlen);
+ hostname[hostlen] = '\0';
+
+ struct hostent *host;
+ host = gethostbyname(hostname); // TODO: Replace this call, as it is obselete.
+ if (host == NULL) {
+ results->data[0] = WASM_I32_VAL(2); // :EnumDependent
+ return NULL;
+ }
+
+ struct sockaddr_in server_addr;
+ memset(&server_addr, 0, sizeof(server_addr));
+
+ server_addr.sin_family = AF_INET; // See comment above
+ memcpy((char *)&server_addr.sin_addr.s_addr, (char *)host->h_addr, host->h_length);
+ server_addr.sin_port = htons(params->data[3].of.i32);
+
+ int result = connect(params->data[0].of.i32, &server_addr, sizeof(server_addr));
+ if (result == 0) results->data[0] = WASM_I32_VAL(0);
+ else results->data[0] = WASM_I32_VAL(3); // :EnumDependent
+
+ return NULL;
+ #endif
+
+ #ifdef _BH_WINDOWS
+ #endif
+}
+
+ONYX_DEF(__net_send, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ #ifdef _BH_LINUX
+ // TODO: The flags at the end should be controllable.
+ int sent = send(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, MSG_NOSIGNAL);
+ results->data[0] = WASM_I32_VAL(sent);
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__net_sendto, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ #ifdef _BH_LINUX
+ struct sockaddr_in dest_addr;
+ int dest_addr_len = sizeof(dest_addr);
+ memset(&dest_addr, 0, dest_addr_len);
+
+ struct onyx_socket_addr *o_addr = (struct onyx_socket_addr *) ONYX_PTR(params->data[3].of.i32);
+ dest_addr.sin_family = AF_INET; // TODO: See other comments related to AF_NET above.
+ dest_addr.sin_port = htons(o_addr->port);
+ dest_addr.sin_addr.s_addr = htonl(o_addr->addr);
+
+ // TODO: The flags at the end should be controllable.
+ int sent = sendto(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, MSG_NOSIGNAL, &dest_addr, dest_addr_len);
+ results->data[0] = WASM_I32_VAL(sent);
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__net_recv, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ *(i32 *) ONYX_PTR(params->data[3].of.i32) = 0;
+
+ #ifdef _BH_LINUX
+ // TODO: The flags at the end should be controllable.
+ int received = recv(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, 0);
+ results->data[0] = WASM_I32_VAL(received);
+
+ if (received < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ *(i32 *) ONYX_PTR(params->data[3].of.i32) = 1;
+ }
+ }
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__net_recvfrom, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ *(i32 *) ONYX_PTR(params->data[4].of.i32) = 0;
+
+ #ifdef _BH_LINUX
+ struct onyx_socket_addr *out_addr = (struct onyx_socket_addr *) ONYX_PTR(params->data[3].of.i32);
+
+ struct sockaddr_in client_addr;
+ int socket_len = sizeof(client_addr);
+ memset(&client_addr, 0, socket_len);
+
+ int received = recvfrom(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, 0, &client_addr, &socket_len);
+ out_addr->family = client_addr.sin_family;
+ out_addr->port = ntohs(client_addr.sin_port);
+ out_addr->addr = ntohl(client_addr.sin_addr.s_addr);
+
+ results->data[0] = WASM_I32_VAL(received);
+
+ if (received < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ *(i32 *) ONYX_PTR(params->data[3].of.i32) = 1;
+ }
+ }
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__net_poll_recv, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), ()) {
+ #ifdef _BH_LINUX
+ int i, res, cursor;
+ struct pollfd* fds;
+
+ fds = alloca(params->data[1].of.i32 * sizeof(struct pollfd));
+
+ for (i=0; i<params->data[1].of.i32; i++) {
+ fds[i].fd = *(i32 *) ONYX_PTR(params->data[0].of.i32 + 4 * i);
+ fds[i].events = POLLIN;
+ fds[i].revents = 0;
+ }
+
+ res = poll(fds, params->data[1].of.i32, params->data[2].of.i32);
+
+ for (i=0; i<params->data[1].of.i32; i++) {
+ *(i32 *) ONYX_PTR(params->data[3].of.i32 + 4 * i) = 0; // NO_CHANGE
+
+ if (fds[i].revents & POLLIN) {
+ *(i32 *) ONYX_PTR(params->data[3].of.i32 + 4 * i) = 1; // READABLE
+ }
+
+ if ((fds[i].revents & POLLHUP)
+ || (fds[i].revents & POLLNVAL)
+ || (fds[i].revents & POLLERR)) {
+ *(i32 *) ONYX_PTR(params->data[3].of.i32 + 4 * i) = 2; // CLOSED
+ }
+ }
+
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__net_host_to_net_s, (WASM_I32), (WASM_I32)) {
+ results->data[0] = WASM_I32_VAL(htons(params->data[0].of.i32));
+ return NULL;
+}
+
+ONYX_DEF(__net_host_to_net_l, (WASM_I32), (WASM_I32)) {
+ results->data[0] = WASM_I32_VAL(htonl(params->data[0].of.i32));
+ return NULL;
+}
+
+ONYX_DEF(__net_net_to_host_s, (WASM_I32), (WASM_I32)) {
+ results->data[0] = WASM_I32_VAL(ntohs(params->data[0].of.i32));
+ return NULL;
+}
+
+ONYX_DEF(__net_net_to_host_l, (WASM_I32), (WASM_I32)) {
+ results->data[0] = WASM_I32_VAL(ntohl(params->data[0].of.i32));
+ return NULL;
+}
+
+
+//
+// C-Pointers
+//
+// These are wildly unsafe and break the core principles of the security
+// of WebAssembly, so there should be a way to turn them off!
+//
+ONYX_DEF(__cptr_make, (WASM_I32), (WASM_I64)) {
+ wasm_val_init_ptr(&results->data[0], ONYX_PTR(params->data[0].of.i32));
+ return NULL;
+}
+
+ONYX_DEF(__cptr_read, (WASM_I64, WASM_I32, WASM_I32), ()) {
+ memcpy(ONYX_PTR(params->data[1].of.i32), (void *) params->data[0].of.i64, params->data[2].of.i32);
+ return NULL;
+}
+
+ONYX_DEF(__cptr_read_u8, (WASM_I64), (WASM_I32)) {
+ results->data[0] = WASM_I32_VAL(*(u8 *) params->data[0].of.i64);
+ return NULL;
+}
+
+ONYX_DEF(__cptr_read_u16, (WASM_I64), (WASM_I32)) {
+ results->data[0] = WASM_I32_VAL(*(u16 *) params->data[0].of.i64);
+ return NULL;
+}
+
+ONYX_DEF(__cptr_read_u32, (WASM_I64), (WASM_I32)) {
+ results->data[0] = WASM_I32_VAL(*(u32 *) params->data[0].of.i64);
+ return NULL;
+}
+
+ONYX_DEF(__cptr_read_u64, (WASM_I64), (WASM_I64)) {
+ results->data[0] = WASM_I64_VAL(*(u64 *) params->data[0].of.i64);
+ return NULL;
+}
+
+ONYX_DEF(__cptr_extract_str, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
+ unsigned int len = strlen((char *) params->data[0].of.i64);
+
+ if (params->data[2].of.i32 != 0) {
+ strncpy(ONYX_PTR(params->data[1].of.i32), (char *) params->data[0].of.i64, params->data[2].of.i32);
+ }
+
+ results->data[0] = WASM_I32_VAL(len);
+ return NULL;
+}
+
+ONYX_LIBRARY {
+ ONYX_FUNC(__file_open_impl)
+ ONYX_FUNC(__file_close)
+ ONYX_FUNC(__file_exists)
+ ONYX_FUNC(__file_stat)
+ ONYX_FUNC(__file_remove)
+ ONYX_FUNC(__file_seek)
+ ONYX_FUNC(__file_tell)
+ ONYX_FUNC(__file_read)
+ ONYX_FUNC(__file_write)
+ ONYX_FUNC(__file_flush)
+ ONYX_FUNC(__file_size)
+ ONYX_FUNC(__file_get_standard)
+ ONYX_FUNC(__file_rename)
+
+ ONYX_FUNC(__dir_open)
+ ONYX_FUNC(__dir_read)
+ ONYX_FUNC(__dir_close)
+ ONYX_FUNC(__dir_create)
+ ONYX_FUNC(__dir_remove)
+
+ ONYX_FUNC(__spawn_thread)
+ ONYX_FUNC(__kill_thread)
+
+ ONYX_FUNC(__process_spawn)
+ ONYX_FUNC(__process_read)
+ ONYX_FUNC(__process_write)
+ ONYX_FUNC(__process_kill)
+ ONYX_FUNC(__process_wait)
+ ONYX_FUNC(__process_destroy)
+
+ ONYX_FUNC(__args_get)
+ ONYX_FUNC(__args_sizes_get)
+
+ ONYX_FUNC(__exit)
+ ONYX_FUNC(__sleep)
+ ONYX_FUNC(__time)
+
+ ONYX_FUNC(__time_localtime)
+ ONYX_FUNC(__time_gmtime)
+ ONYX_FUNC(__time_strftime)
+ ONYX_FUNC(__time_strptime)
+
+ ONYX_FUNC(__net_create_socket)
+ ONYX_FUNC(__net_close_socket)
+ ONYX_FUNC(__net_setting)
+ ONYX_FUNC(__net_bind)
+ ONYX_FUNC(__net_listen)
+ ONYX_FUNC(__net_accept)
+ ONYX_FUNC(__net_connect_unix)
+ ONYX_FUNC(__net_connect_ipv4)
+ ONYX_FUNC(__net_send)
+ ONYX_FUNC(__net_sendto)
+ ONYX_FUNC(__net_recv)
+ ONYX_FUNC(__net_recvfrom)
+ ONYX_FUNC(__net_poll_recv)
+ ONYX_FUNC(__net_host_to_net_s)
+ ONYX_FUNC(__net_host_to_net_l)
+ ONYX_FUNC(__net_net_to_host_s)
+ ONYX_FUNC(__net_net_to_host_l)
+
+ ONYX_FUNC(__cptr_make)
+ ONYX_FUNC(__cptr_read)
+ ONYX_FUNC(__cptr_read_u8)
+ ONYX_FUNC(__cptr_read_u16)
+ ONYX_FUNC(__cptr_read_u32)
+ ONYX_FUNC(__cptr_read_u64)
+ ONYX_FUNC(__cptr_extract_str)
+
+ NULL
+};
--- /dev/null
+
+# The base path for the installation.
+# This is typcially /usr or /usr/local.
+INSTALL_DIR="/usr"
+
+# Where the core libraries for Onyx will go.
+CORE_DIR="$INSTALL_DIR/share/onyx"
+
+# Where the onyx executable will be placed.
+BIN_DIR="$INSTALL_DIR/bin"
+
+# The compiler to use. Only GCC and TCC have been tested.
+CC='gcc'
+
+# The architecture of your system. If your not sure, leave this alone.
+ARCH="$(uname -m)"
+
+RUNTIME_LIBRARY="ovmwasm"
+# RUNTIME_LIBRARY="wasmer"
+
+# Where the Wasmer library files can be found.
+# They are bundled with the project, but if a different version is available, these can be changed.
+WASMER_INCLUDE_DIR="$(pwd)/lib/common/include"
+WASMER_LIBRARY_DIR="$(pwd)/lib/linux_$ARCH/lib"
--- /dev/null
+#ifndef BH_H
+#define BH_H
+
+// NOTE: For lseek64
+#define _LARGEFILE64_SOURCE
+
+#if defined(_WIN32) || defined(_WIN64)
+ #define _BH_WINDOWS 1
+#endif
+
+#if defined(__unix__)
+ #define _BH_LINUX 1
+#endif
+
+#include <sys/stat.h>
+#include <malloc.h>
+#include <time.h>
+
+#ifdef _BH_LINUX
+ #include <errno.h>
+ #include <fcntl.h>
+ #include <unistd.h>
+ #include <dirent.h>
+#endif
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h> // TODO: Replace with needed functions
+#include <stdint.h>
+#include <assert.h>
+#include <stdio.h>
+
+#if defined(_MSC_VER) && !defined(_WINDOWS_)
+ #include "small_windows.h"
+#endif
+
+//-------------------------------------------------------------------------------------
+// Better types
+//-------------------------------------------------------------------------------------
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+typedef int8_t i8;
+typedef int16_t i16;
+typedef int32_t i32;
+typedef int64_t i64;
+typedef int64_t isize;
+typedef i32 b32;
+typedef void* ptr;
+typedef float f32;
+typedef double f64;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better character functions
+//-------------------------------------------------------------------------------------
+b32 char_is_alpha(const char a);
+b32 char_is_num(const char a);
+b32 char_is_alphanum(const char a);
+char charset_contains(const char* charset, char ch);
+b32 char_is_whitespace(const char a);
+b32 char_in_range(const char lo, const char hi, const char a);
+i64 chars_match(char* ptr1, char* ptr2);
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better math functions
+//-------------------------------------------------------------------------------------
+#define bh_max(a, b) ((a) > (b) ? (a) : (b))
+#define bh_min(a, b) ((a) < (b) ? (a) : (b))
+#define bh_clamp(v, a, b) (bh_min((b), bh_max((a), (v))))
+#define bh_abs(x) ((x) < 0 ? -(x) : (x))
+#define size_of(x) (isize) sizeof(x)
+
+static inline u64 log2_dumb(u64 n) {
+ switch (n) {
+ case 1 << 0: return 0;
+ case 1 << 1: return 1;
+ case 1 << 2: return 2;
+ case 1 << 3: return 3;
+ case 1 << 4: return 4;
+ case 1 << 5: return 5;
+ case 1 << 6: return 6;
+ case 1 << 7: return 7;
+ case 1 << 8: return 8;
+ case 1 << 9: return 9;
+ case 1 << 10: return 10;
+ case 1 << 11: return 11;
+ case 1 << 12: return 12;
+ case 1 << 13: return 13;
+ case 1 << 14: return 14;
+ case 1 << 15: return 15;
+ case 1 << 16: return 16;
+ case 1 << 17: return 17;
+ case 1 << 18: return 18;
+ case 1 << 19: return 19;
+ case 1 << 20: return 20;
+ case 1 << 21: return 21;
+ case 1 << 22: return 22;
+ case 1 << 23: return 23;
+ case 1 << 24: return 24;
+ case 1 << 25: return 25;
+ case 1 << 26: return 26;
+ case 1 << 27: return 27;
+ case 1 << 28: return 28;
+ case 1 << 29: return 29;
+ case 1 << 30: return 30;
+ case 1 << 31: return 31;
+
+ default: return 0;
+ }
+}
+
+
+
+static inline const char* bh_num_suffix(u64 i) {
+ if (i == 11 || i == 12 || i == 13) return "th";
+
+ switch (i % 10) {
+ case 0: return "th";
+ case 1: return "st";
+ case 2: return "nd";
+ case 3: return "rd";
+ case 4: return "th";
+ case 5: return "th";
+ case 6: return "th";
+ case 7: return "th";
+ case 8: return "th";
+ case 9: return "th";
+
+ default: return "";
+ }
+}
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Conversion functions
+//-------------------------------------------------------------------------------------
+
+// Converts an unsigned integer to the unsigned LEB128 format
+u8* uint_to_uleb128(u64 n, i32* output_length);
+u8* int_to_leb128(i64 n, i32* output_length);
+u8* float_to_ieee754(f32 f, b32 reverse);
+u8* double_to_ieee754(f64 f, b32 reverse);
+
+u64 uleb128_to_uint(u8* bytes, i32 *byte_walker);
+
+
+
+//-------------------------------------------------------------------------------------
+// Helpful macros
+//-------------------------------------------------------------------------------------
+#define bh_offset_of(Type, elem) ((isize)&(((Type)*) 0)->elem)
+#define bh_align_of(Type) bh_offset_of(struct { char c; Type member; }, member)
+#define bh_swap(Type, a, b) do { Type tmp = (a); (a) = (b); (b) = tmp; } while(0)
+
+#define bh_align(x, a) if ((x) % (a) != 0) (x) += (a) - ((x) % (a));
+
+#define bh_pointer_add(ptr, amm) ((void *)((u8 *) (ptr) + (amm)))
+#define BH_BIT(x) (1 << (x))
+#define BH_MASK_SET(var, set, mask) ((set) ? ((var) |= (mask)) : ((var) &= ~(mask)))
+
+#define fori(var, lo, hi) for (i64 var = (lo); var < (hi); var++)
+#define forir(var, hi, lo) for (i64 var = (hi); var >= (lo); var--)
+#define forll(T, var, start, step) for (T* var = (start); var != NULL; var = (T *) var->step)
+
+#if defined(BH_DEBUG) && !defined(_BH_WINDOWS)
+ #define DEBUG_HERE __asm("int $3")
+#else
+ #define DEBUG_HERE
+#endif
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Custom allocators
+//-------------------------------------------------------------------------------------
+
+typedef enum bh_allocator_actions {
+ bh_allocator_action_alloc,
+ bh_allocator_action_free,
+ bh_allocator_action_resize,
+} bh_allocator_actions;
+
+#define BH_ALLOCATOR_PROC(name) \
+ptr name(ptr data, bh_allocator_actions action, \
+ isize size, isize alignment, \
+ void* prev_memory, \
+ u64 flags)
+
+typedef BH_ALLOCATOR_PROC(bh__allocator_proc); // NOTE: so bh__allocator_proc can be used instead of that type
+
+typedef struct bh_allocator {
+ bh__allocator_proc* proc; // Procedure that can handle bh_allocator_actions
+ ptr data; // Pointer to the other data for the allocator
+} bh_allocator;
+
+typedef enum bh_allocator_flags {
+ bh_allocator_flag_clear = 1 // Sets all memory to be 0
+} bh_allocator_flags;
+
+ptr bh_alloc(bh_allocator a, isize size);
+ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment);
+ptr bh_resize(bh_allocator a, ptr data, isize new_size);
+ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment);
+void bh_free(bh_allocator a, ptr data);
+
+#define bh_alloc_item(allocator_, T) (T *) bh_alloc(allocator_, sizeof(T))
+#define bh_alloc_array(allocator_, T, n) (T *) bh_alloc(allocator_, sizeof(T) * (n))
+
+// NOTE: This should get optimized out since alignment should be a power of two
+#define bh__align(x, alignment) ((((x) / alignment) + 1) * alignment)
+
+
+
+
+// HEAP ALLOCATOR
+// Essentially a wrapper for malloc, free and realloc
+bh_allocator bh_heap_allocator(void);
+BH_ALLOCATOR_PROC(bh_heap_allocator_proc);
+
+
+
+
+
+// ARENA ALLOCATOR
+typedef struct bh_arena {
+ bh_allocator backing;
+ ptr first_arena, current_arena;
+ isize size, arena_size; // in bytes
+} bh_arena;
+
+typedef struct bh__arena_internal {
+ ptr next_arena;
+ void* data; // Not actually a pointer, just used for the offset
+} bh__arena_internal;
+
+void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size);
+void bh_arena_free(bh_arena* alloc);
+bh_allocator bh_arena_allocator(bh_arena* alloc);
+BH_ALLOCATOR_PROC(bh_arena_allocator_proc);
+
+
+
+
+
+
+// SCRATCH ALLOCATOR
+typedef struct bh_scratch {
+ bh_allocator backing;
+ ptr memory, end, curr;
+} bh_scratch;
+
+void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size);
+void bh_scratch_free(bh_scratch* scratch);
+bh_allocator bh_scratch_allocator(bh_scratch* scratch);
+BH_ALLOCATOR_PROC(bh_scratch_allocator_proc);
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Allocator based string functions
+//-------------------------------------------------------------------------------------
+
+b32 bh_str_starts_with(char* str, char* start);
+b32 bh_str_ends_with(char* str, char* end);
+char* bh_strdup(bh_allocator a, char* str);
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better files
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_FILE
+
+typedef enum bh_file_error {
+ BH_FILE_ERROR_NONE,
+ BH_FILE_ERROR_INVALID,
+ BH_FILE_ERROR_BAD_FD,
+} bh_file_error;
+
+typedef enum bh_file_mode {
+ BH_FILE_MODE_READ = 1 << 0,
+ BH_FILE_MODE_WRITE = 1 << 1,
+ BH_FILE_MODE_APPEND = 1 << 2,
+ BH_FILE_MODE_RW = 1 << 3,
+
+ BH_FILE_MODE_MODES = BH_FILE_MODE_READ | BH_FILE_MODE_WRITE | BH_FILE_MODE_APPEND | BH_FILE_MODE_RW
+} bh_file_mode;
+
+typedef enum bh_file_whence {
+ BH_FILE_WHENCE_BEGIN = SEEK_SET,
+ BH_FILE_WHENCE_CURRENT = SEEK_CUR,
+ BH_FILE_WHENCE_END = SEEK_END,
+} bh_file_whence;
+
+#ifdef _BH_WINDOWS
+ typedef HANDLE bh_file_descriptor;
+#else
+ typedef int bh_file_descriptor;
+#endif
+
+typedef struct bh_file {
+ bh_file_descriptor fd;
+ char const* filename;
+} bh_file;
+
+typedef enum bh_file_standard {
+ BH_FILE_STANDARD_INPUT,
+ BH_FILE_STANDARD_OUTPUT,
+ BH_FILE_STANDARD_ERROR
+} bh_file_standard;
+
+typedef struct bh_file_contents {
+ bh_allocator allocator;
+ const char *filename;
+ isize length;
+ void* data;
+ isize line_count;
+} bh_file_contents;
+
+
+#define BH_FILE_TYPE_DIRECTORY 3
+#define BH_FILE_TYPE_FILE 4
+#define BH_FILE_TYPE_LINK 5
+typedef struct bh_file_stats {
+ isize size;
+ u32 file_type;
+} bh_file_stats;
+
+bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand);
+
+bh_file_error bh_file_create(bh_file* file, char const* filename);
+bh_file_error bh_file_open(bh_file* file, char const* filename);
+bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename);
+bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename);
+b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read);
+b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote);
+i64 bh_file_seek(bh_file* file, i64 offset, bh_file_whence whence);
+i64 bh_file_seek_to(bh_file* file, i64 offset);
+i64 bh_file_seek_to_end(bh_file* file);
+i64 bh_file_skip(bh_file* file, i64 bytes);
+i64 bh_file_tell(bh_file* file);
+bh_file_error bh_file_close(bh_file* file);
+i32 bh_file_read(bh_file* file, void* buffer, isize buff_size);
+i32 bh_file_write(bh_file* file, void* buffer, isize buff_size);
+void bh_file_flush(bh_file* file);
+i64 bh_file_size(bh_file* file);
+b32 bh_file_stat(char const* filename, bh_file_stats* stat);
+b32 bh_file_exists(char const* filename);
+b32 bh_file_remove(char const* filename);
+char* bh_path_get_full_name(char const* filename, bh_allocator a);
+char* bh_path_get_parent(char const* filename, bh_allocator a);
+char* bh_path_convert_separators(char* path);
+
+// This function returns a volatile pointer. Do not store it without copying!
+// `included_folders` is bh_arr(const char *).
+char* bh_lookup_file(char* filename, char* relative_to, char *suffix, b32 add_suffix, const char ** included_folders, b32 search_included_folders);
+
+#define bh_file_read_contents(allocator_, x) _Generic((x), \
+ bh_file*: bh_file_read_contents_bh_file, \
+ const char*: bh_file_read_contents_direct, \
+ char*: bh_file_read_contents_direct)((allocator_), x)
+
+bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file);
+bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename);
+i32 bh_file_contents_free(bh_file_contents* contents);
+
+
+#ifdef _BH_WINDOWS
+ typedef struct Windows_Directory_Opened {
+ HANDLE hndl;
+ WIN32_FIND_DATAA found_file;
+ } Windows_Directory_Opened;
+
+ typedef Windows_Directory_Opened *bh_dir;
+#else
+ typedef DIR *bh_dir;
+#endif
+
+typedef enum bh_dirent_type {
+ BH_DIRENT_UNKNOWN,
+ BH_DIRENT_BLOCK,
+ BH_DIRENT_CHAR,
+ BH_DIRENT_DIRECTORY,
+ BH_DIRENT_FILE,
+ BH_DIRENT_SYMLINK,
+ BH_DIRENT_OTHER,
+} bh_dirent_type;
+
+typedef struct bh_dirent {
+ bh_dirent_type type;
+ u32 id;
+ u32 name_length;
+ char name[256];
+} bh_dirent;
+
+bh_dir bh_dir_open(char* path);
+b32 bh_dir_read(bh_dir dir, bh_dirent* out);
+void bh_dir_close(bh_dir dir);
+
+#endif
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Alternate printing
+//-------------------------------------------------------------------------------------
+// Barebones implementation of printf. Does not support all format options
+// Currently supports:
+// %c - chars
+// %_(u)d - ints where _ is:
+// nothing - decimal
+// o - octal
+// x - hexadecimal
+// %_(u)l - longs where _ is:
+// nothing - decimal
+// o - octal
+// x - hexadecimal
+// %f - floating points
+// %s - null terminated strings
+// %p - pointers
+// %% - literal %
+
+typedef struct bh__print_format {
+ u32 base;
+} bh__print_format;
+
+isize bh_printf(char const *fmt, ...);
+isize bh_printf_va(char const *fmt, va_list va);
+isize bh_printf_err(char const *fmt, ...);
+isize bh_printf_err_va(char const *fmt, va_list va);
+isize bh_fprintf(bh_file* f, char const *fmt, ...);
+isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va);
+char* bh_bprintf(char const *fmt, ...);
+char* bh_bprintf_va(char const *fmt, va_list va);
+char* bh_aprintf(bh_allocator alloc, const char* fmt, ...);
+char* bh_aprintf_va(bh_allocator alloc, const char* fmt, va_list va);
+isize bh_snprintf(char *str, isize n, char const *fmt, ...);
+isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Flexible buffer
+//-------------------------------------------------------------------------------------
+
+typedef struct bh_buffer {
+ bh_allocator allocator;
+ i32 length, capacity;
+ u8* data;
+} bh_buffer;
+
+#ifndef BH_BUFFER_GROW_FORMULA
+#define BH_BUFFER_GROW_FORMULA(x) ((x) > 0 ? ((x) << 1) : 16)
+#endif
+
+void bh_buffer_init(bh_buffer* buffer, bh_allocator alloc, i32 length);
+void bh_buffer_free(bh_buffer* buffer);
+void bh_buffer_clear(bh_buffer* buffer);
+void bh_buffer_grow(bh_buffer* buffer, i32 length);
+void bh_buffer_append(bh_buffer* buffer, const void * data, i32 length);
+void bh_buffer_concat(bh_buffer* buffer, bh_buffer other);
+void bh_buffer_write_byte(bh_buffer* buffer, u8 byte);
+void bh_buffer_write_u32(bh_buffer* buffer, u32 i);
+void bh_buffer_write_u64(bh_buffer* buffer, u64 i);
+void bh_buffer_align(bh_buffer* buffer, u32 alignment);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better dynamically-sized arrays
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_ARRAY
+
+typedef struct bh__arr {
+ bh_allocator allocator;
+ i32 length, capacity;
+} bh__arr;
+
+#ifndef BH_ARR_GROW_FORMULA
+#define BH_ARR_GROW_FORMULA(x) ((x) > 0 ? ((x) << 1) : 4)
+#endif
+
+#define bh_arr(T) T*
+#define bh__arrhead(arr) (((bh__arr *)(arr)) - 1)
+
+#define bh_arr_allocator(arr) (arr ? bh__arrhead(arr)->allocator : bh_heap_allocator())
+#define bh_arr_length(arr) (arr ? bh__arrhead(arr)->length : 0)
+#define bh_arr_capacity(arr) (arr ? bh__arrhead(arr)->capacity : 0)
+#define bh_arr_size(arr) (arr ? bh__arrhead(arr)->capacity * sizeof(*(arr)) : 0)
+#define bh_arr_valid(arr, i) (arr ? (i32)(i) < bh__arrhead(arr)->length : 0)
+
+#define bh_arr_pop(arr) ((arr)[--bh__arrhead(arr)->length])
+#define bh_arr_last(arr) ((arr)[bh__arrhead(arr)->length - 1])
+#define bh_arr_end(arr, i) ((i) >= &(arr)[bh_arr_length(arr)])
+#define bh_arr_start(arr, i) ((i) < (arr))
+
+#define bh_arr_new(allocator_, arr, cap) (bh__arr_grow((allocator_), (void**) &(arr), sizeof(*(arr)), cap))
+#define bh_arr_free(arr) (bh__arr_free((void**) &(arr)))
+#define bh_arr_copy(allocator_, arr) (bh__arr_copy((allocator_), (arr), sizeof(*(arr))))
+
+#define bh_arr_grow(arr, cap) (bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), cap))
+#define bh_arr_shrink(arr, cap) (bh__arr_shrink((void **) &(arr), sizeof(*(arr)), cap))
+#define bh_arr_set_length(arr, n) (bh__arrhead(arr)->length = n)
+
+#define bh_arr_insertn(arr, i, n) (bh__arr_insertn((void **) &(arr), sizeof(*(arr)), i, n))
+
+#define bh_arr_insert_end(arr, n) ( \
+ bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + n), \
+ bh__arrhead(arr)->length += n)
+
+#define bh_arr_push(arr, value) ( \
+ bh_arr_length(arr) + 1 > bh_arr_capacity(arr) ? bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + 1) : 0, \
+ arr[bh__arrhead(arr)->length++] = value)
+
+#define bh_arr_set_at(arr, n, value) ( \
+ bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), (n) + 1), \
+ bh_arr_set_length((arr), bh_max(bh_arr_length(arr), (i32) (n) + 1)), \
+ arr[n] = value)
+
+#define bh_arr_is_empty(arr) (arr ? bh__arrhead(arr)->length == 0 : 1)
+#define bh_arr_clear(arr) (arr ? (bh__arrhead(arr)->length = 0) : 0)
+
+#define bh_arr_deleten(arr, i, n) (bh__arr_deleten((void **) &(arr), sizeof(*(arr)), i, n))
+#define bh_arr_fastdelete(arr, i) (arr[i] = arr[--bh__arrhead(arr)->length])
+
+#define bh_arr_each(T, var, arr) for (T* var = (arr); !bh_arr_end((arr), var); var++)
+#define bh_arr_rev_each(T, var, arr) for (T* var = &bh_arr_last((arr)); !bh_arr_start((arr), var); var--)
+
+#define bh_arr_zero(arr) memset(arr, 0, bh_arr_length(arr) * sizeof(*(arr)));
+
+b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap);
+b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap);
+b32 bh__arr_free(void **arr);
+void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize);
+void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems);
+void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems);
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// STRING HASH TABLE FUNCTIONS
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_TABLE
+
+#ifdef BH_DEFINE
+u64 bh__table_hash_function(const char* str, i32 len, i32 mod) {
+ u64 hash = 5381;
+ i32 c, l = 0;
+ if (len == 0) len = ((u32) 1 << 31) - 1;
+
+ while ((c = *str++) && l++ < len) {
+ hash = (hash << 5) + hash + c;
+ }
+
+ return hash % mod;
+}
+#endif
+
+typedef struct bh_table_iterator {
+ ptr *tab, *endtab;
+ i32 elemsize, arrlen;
+ ptr entry;
+} bh_table_iterator;
+
+typedef struct bh__table {
+ bh_allocator allocator;
+ u64 table_size; // NOTE: u64 since padding will make it 8-bytes no matter what
+ ptr arrs[];
+} bh__table;
+
+#define bh_table(T) T*
+
+#ifdef BH_TABLE_SIZE_SAFE
+ #define bh_table_init(allocator_, tab, hs) bh__table_init(allocator_, (bh__table **)&(tab), hs)
+ #define bh_table_free(tab) bh__table_free((bh__table **)&(tab))
+ #define bh_table_put(T, tab, key, value) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__table_put((bh__table *) tab, sizeof(T), key)) = (T) value))
+ #define bh_table_has(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (bh__table_has((bh__table *) tab, sizeof(T), key)))
+ #define bh_table_get(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__table_get((bh__table *) tab, sizeof(T), key))))
+ #define bh_table_delete(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), bh__table_delete((bh__table *) tab, sizeof(T), key))
+ #define bh_table_clear(tab) (bh__table_clear((bh__table *) tab))
+
+ #define bh_table_iter_setup(T, tab) (assert(sizeof(T) == sizeof(*(tab))), bh__table_iter_setup((bh__table *) tab, sizeof(T)))
+ #define bh_table_iter_key(it) ((char *)(bh_pointer_add(it.entry, it.elemsize + sizeof(u16))))
+ #define bh_table_iter_value(T, it) (*(T *)it.entry)
+#else
+ #define bh_table_init(allocator_, tab, hs) bh__table_init(allocator_, (bh__table **)&(tab), hs)
+ #define bh_table_free(tab) bh__table_free((bh__table **)&(tab))
+ #define bh_table_put(T, tab, key, value) (*((T *) bh__table_put((bh__table *) tab, sizeof(T), key)) = value)
+ #define bh_table_has(T, tab, key) (bh__table_has((bh__table *) tab, sizeof(T), key))
+ #define bh_table_get(T, tab, key) (*((T *) bh__table_get((bh__table *) tab, sizeof(T), key)))
+ #define bh_table_delete(T, tab, key) (bh__table_delete((bh__table *) tab, sizeof(T), key))
+ #define bh_table_clear(tab) (bh__table_clear((bh__table *) tab))
+
+ #define bh_table_iter_setup(T, tab) (bh__table_iter_setup((bh__table *) tab, sizeof(T)))
+ #define bh_table_iter_key(it) ((char *)(bh_pointer_add(it.entry, it.elemsize + sizeof(u16))))
+ #define bh_table_iter_value(T, it) (*(T *)it.entry)
+#endif
+
+#define bh_table_each_start(T, table) { \
+ bh_table_iterator it = bh_table_iter_setup(T, (table)); \
+ while (bh_table_iter_next(&it)) { \
+ const char* key = bh_table_iter_key(it); \
+ T value = bh_table_iter_value(T, it);
+#define bh_table_each_end } }
+
+b32 bh__table_init(bh_allocator allocator, bh__table **table, i32 table_size);
+b32 bh__table_free(bh__table **table);
+ptr bh__table_put(bh__table *table, i32 elemsize, char *key);
+b32 bh__table_has(bh__table *table, i32 elemsize, char *key);
+ptr bh__table_get(bh__table *table, i32 elemsize, char *key);
+void bh__table_delete(bh__table *table, i32 elemsize, char *key);
+void bh__table_clear(bh__table *table);
+bh_table_iterator bh__table_iter_setup(bh__table *table, i32 elemsize);
+b32 bh_table_iter_next(bh_table_iterator* it);
+
+#endif
+// Using stb_ds for tables now because they are better in every single way.
+#define Table(T) struct { char *key; T value; } *
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------
+// IMAP (integer to integer map)
+//-------------------------------------------------------------------------------
+#ifndef BH_NO_IMAP
+
+typedef u64 bh_imap_entry_t;
+
+typedef struct bh__imap_lookup_result {
+ i64 hash_index;
+ i64 entry_prev;
+ i64 entry_index;
+} bh__imap_lookup_result;
+
+typedef struct bh__imap_entry {
+ bh_imap_entry_t key, value;
+ i64 next;
+} bh__imap_entry;
+
+typedef struct bh_imap {
+ bh_allocator allocator;
+
+ bh_arr(i64) hashes;
+ bh_arr(bh__imap_entry) entries;
+} bh_imap;
+
+
+void bh_imap_init(bh_imap* imap, bh_allocator alloc, i32 hash_count);
+void bh_imap_free(bh_imap* imap);
+void bh_imap_put(bh_imap* imap, bh_imap_entry_t key, bh_imap_entry_t value);
+b32 bh_imap_has(bh_imap* imap, bh_imap_entry_t key);
+bh_imap_entry_t bh_imap_get(bh_imap* imap, bh_imap_entry_t key);
+void bh_imap_delete(bh_imap* imap, bh_imap_entry_t key);
+void bh_imap_clear(bh_imap* imap);
+
+#ifdef BH_DEFINE
+#endif // BH_DEFINE
+
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+// MANAGED HEAP ALLOCATOR
+typedef struct bh_managed_heap {
+ bh_imap ptrs;
+} bh_managed_heap;
+
+void bh_managed_heap_init(bh_managed_heap* mh);
+void bh_managed_heap_free(bh_managed_heap* mh);
+bh_allocator bh_managed_heap_allocator(bh_managed_heap* mh);
+BH_ALLOCATOR_PROC(bh_managed_heap_allocator_proc);
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------
+// OTHER COMMON DATA STRUCTURES
+//-------------------------------------------------------------------------------
+#ifndef BH_NO_DATASTRUCTURES
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#endif // BH_NO_DATASTRUCTURES
+
+
+
+
+
+//------------------------------------------------------------------------------
+// TIME / DURATION
+//------------------------------------------------------------------------------
+u64 bh_time_curr();
+u64 bh_time_duration(u64 old);
+
+
+
+
+
+
+
+
+
+
+
+#ifdef BH_DEFINE
+
+#undef BH_DEFINE
+//-------------------------------------------------------------------------------------
+// IMPLEMENTATIONS
+//-------------------------------------------------------------------------------------
+
+//-------------------------------------------------------------------------------------
+// CHAR FUNCTIONS
+//-------------------------------------------------------------------------------------
+
+b32 char_is_alpha(const char a) {
+ return ('a' <= a && a <= 'z') || ('A' <= a && a <= 'Z');
+}
+
+char charset_contains(const char* charset, char ch) {
+ while (*charset) {
+ if (*charset == ch) return ch;
+ charset++;
+ }
+
+ return 0;
+}
+
+b32 char_is_num(const char a) {
+ return ('0' <= a && a <= '9');
+}
+
+b32 char_is_alphanum(const char a) {
+ return char_is_alpha(a) || char_is_num(a);
+}
+
+b32 char_is_whitespace(const char a) {
+ return charset_contains(" \t\r\n", a);
+}
+
+b32 char_in_range(const char lo, const char hi, const char a) {
+ return lo <= a && a <= hi;
+}
+
+i64 chars_match(char* ptr1, char* ptr2) {
+ i64 len = 0;
+ while (*ptr2 != '\0' && *ptr1 == *ptr2) ptr1++, ptr2++, len++;
+ return *ptr2 == '\0' ? len : 0;
+}
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// CUSTOM ALLOCATORS IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+ptr bh_alloc(bh_allocator a, isize size) {
+ return bh_alloc_aligned(a, size, 16);
+}
+
+ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment) {
+ return a.proc(a.data, bh_allocator_action_alloc, size, alignment, NULL, 0);
+}
+
+ptr bh_resize(bh_allocator a, ptr data, isize new_size) {
+ return bh_resize_aligned(a, data, new_size, 16);
+}
+
+ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment) {
+ return a.proc(a.data, bh_allocator_action_resize, new_size, alignment, data, 0);
+}
+
+void bh_free(bh_allocator a, ptr data) {
+ if (data != NULL) a.proc(a.data, bh_allocator_action_free, 0, 0, data, 0);
+}
+
+
+
+// HEAP ALLOCATOR IMPLEMENTATION
+bh_allocator bh_heap_allocator(void) {
+ return (bh_allocator) {
+ .proc = bh_heap_allocator_proc,
+ .data = NULL
+ };
+}
+
+BH_ALLOCATOR_PROC(bh_heap_allocator_proc) {
+ ptr retval = NULL;
+
+ switch (action) {
+ case bh_allocator_action_alloc: {
+#if defined(_BH_WINDOWS)
+ retval = _aligned_malloc(size, alignment);
+#elif defined(_BH_LINUX)
+ i32 success = posix_memalign(&retval, alignment, size);
+#endif
+ if (flags & bh_allocator_flag_clear && retval != NULL) {
+ memset(retval, 0, size);
+ }
+ } break;
+
+ case bh_allocator_action_resize: {
+ // TODO: Maybe replace with better custom function
+#if defined(_BH_WINDOWS)
+ retval = _aligned_realloc(prev_memory, size, alignment);
+#elif defined(_BH_LINUX)
+ retval = realloc(prev_memory, size);
+#endif
+ } break;
+
+ case bh_allocator_action_free: {
+#if defined(_BH_WINDOWS)
+ _aligned_free(prev_memory);
+#elif defined(_BH_LINUX)
+ free(prev_memory);
+#endif
+ } break;
+ }
+
+ return retval;
+}
+
+
+
+
+
+
+// MANAGED HEAP ALLOCATOR IMPLEMENTATION
+void bh_managed_heap_init(bh_managed_heap* mh) {
+ bh_imap_init(&mh->ptrs, bh_heap_allocator(), 512);
+}
+
+void bh_managed_heap_free(bh_managed_heap* mh) {
+ bh_arr_each(bh__imap_entry, p, mh->ptrs.entries) {
+#if defined(_BH_WINDOWS)
+ _aligned_free((void *) p->key);
+#elif defined(_BH_LINUX)
+ free((void *) p->key);
+#endif
+ }
+
+ bh_imap_free(&mh->ptrs);
+}
+
+bh_allocator bh_managed_heap_allocator(bh_managed_heap* mh) {
+ return (bh_allocator) {
+ .proc = bh_managed_heap_allocator_proc,
+ .data = mh
+ };
+}
+
+BH_ALLOCATOR_PROC(bh_managed_heap_allocator_proc) {
+ bh_managed_heap* mh = (bh_managed_heap *) data;
+ ptr retval = NULL;
+
+ switch (action) {
+ case bh_allocator_action_alloc: {
+#if defined(_BH_WINDOWS)
+ retval = _aligned_malloc(size, alignment);
+#elif defined(_BH_LINUX)
+ i32 success = posix_memalign(&retval, alignment, size);
+#endif
+
+ if (flags & bh_allocator_flag_clear && retval != NULL) {
+ memset(retval, 0, size);
+ }
+
+ if (retval != NULL)
+ bh_imap_put(&mh->ptrs, (u64) retval, 1);
+ } break;
+
+ case bh_allocator_action_resize: {
+ bh_imap_delete(&mh->ptrs, (u64) prev_memory);
+#if defined(_BH_WINDOWS)
+ retval = _aligned_realloc(prev_memory, size, alignment);
+#elif defined(_BH_LINUX)
+ retval = realloc(prev_memory, size);
+#endif
+ bh_imap_put(&mh->ptrs, (u64) retval, 1);
+ } break;
+
+ case bh_allocator_action_free: {
+ bh_imap_delete(&mh->ptrs, (u64) prev_memory);
+#if defined(_BH_WINDOWS)
+ _aligned_free(prev_memory);
+#elif defined(_BH_LINUX)
+ free(prev_memory);
+#endif
+ } break;
+ }
+
+ return retval;
+}
+
+
+
+
+
+
+
+
+// ARENA ALLOCATOR IMPLEMENTATION
+void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size) {
+ arena_size = bh_max(arena_size, size_of(ptr));
+ ptr data = bh_alloc(backing, arena_size);
+
+ alloc->backing = backing;
+ alloc->arena_size = arena_size;
+ alloc->size = sizeof(ptr);
+ alloc->first_arena = data;
+ alloc->current_arena = data;
+
+ ((bh__arena_internal *)(alloc->first_arena))->next_arena = NULL;
+}
+
+void bh_arena_free(bh_arena* alloc) {
+ bh__arena_internal *walker = (bh__arena_internal *) alloc->first_arena;
+ bh__arena_internal *trailer = walker;
+ while (walker != NULL) {
+ walker = walker->next_arena;
+ bh_free(alloc->backing, trailer);
+ trailer = walker;
+ }
+
+ alloc->first_arena = NULL;
+ alloc->current_arena = NULL;
+ alloc->arena_size = 0;
+ alloc->size = 0;
+}
+
+bh_allocator bh_arena_allocator(bh_arena* alloc) {
+ return (bh_allocator) {
+ .proc = bh_arena_allocator_proc,
+ .data = alloc,
+ };
+}
+
+BH_ALLOCATOR_PROC(bh_arena_allocator_proc) {
+ bh_arena* alloc_arena = (bh_arena*) data;
+
+ ptr retval = NULL;
+
+ switch (action) {
+ case bh_allocator_action_alloc: {
+ bh_align(size, alignment);
+ bh_align(alloc_arena->size, alignment);
+
+ if (size > alloc_arena->arena_size - size_of(ptr)) {
+ // Size too large for the arena
+ return NULL;
+ }
+
+ if (alloc_arena->size + size >= alloc_arena->arena_size) {
+ bh__arena_internal* new_arena = (bh__arena_internal *) bh_alloc(alloc_arena->backing, alloc_arena->arena_size);
+
+ if (new_arena == NULL) {
+ bh_printf_err("Arena Allocator: couldn't allocate new arena");
+ return NULL;
+ }
+
+ new_arena->next_arena = NULL;
+ ((bh__arena_internal *)(alloc_arena->current_arena))->next_arena = new_arena;
+ alloc_arena->current_arena = new_arena;
+ alloc_arena->size = sizeof(ptr);
+ }
+
+ retval = bh_pointer_add(alloc_arena->current_arena, alloc_arena->size);
+ alloc_arena->size += size;
+ } break;
+
+ case bh_allocator_action_resize: {
+ // Do nothing since this is a fixed allocator
+ } break;
+
+ case bh_allocator_action_free: {
+ // Do nothing since this allocator isn't made for freeing memory
+ } break;
+ }
+
+ return retval;
+}
+
+
+
+
+// SCRATCH ALLOCATOR IMPLEMENTATION
+void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size) {
+ ptr memory = bh_alloc(backing, scratch_size);
+
+ scratch->backing = backing;
+ scratch->memory = memory;
+ scratch->curr = memory;
+ scratch->end = bh_pointer_add(memory, scratch_size);
+}
+
+void bh_scratch_free(bh_scratch* scratch) {
+ bh_free(scratch->backing, scratch->memory);
+
+ scratch->memory = NULL;
+ scratch->curr = NULL;
+ scratch->end = NULL;
+}
+
+bh_allocator bh_scratch_allocator(bh_scratch* scratch) {
+ return (bh_allocator) {
+ .proc = bh_scratch_allocator_proc,
+ .data = scratch,
+ };
+}
+
+BH_ALLOCATOR_PROC(bh_scratch_allocator_proc) {
+ bh_scratch* scratch = (bh_scratch*) data;
+ ptr retval = NULL;
+
+ switch (action) {
+ case bh_allocator_action_alloc: {
+ if (size > ((u8 *) scratch->end) - ((u8 *) scratch->memory)) {
+ return NULL;
+ }
+
+ retval = scratch->curr;
+ scratch->curr = bh_pointer_add(scratch->curr, size);
+
+ if (scratch->curr >= scratch->end) {
+ scratch->curr = scratch->memory;
+ retval = scratch->curr;
+ }
+ } break;
+
+ case bh_allocator_action_free: break;
+
+ case bh_allocator_action_resize: {
+ if (size > ((u8 *) scratch->end) - ((u8 *) scratch->memory)) {
+ return NULL;
+ }
+
+ retval = scratch->curr;
+ scratch->curr = bh_pointer_add(scratch->curr, size);
+
+ if (scratch->curr >= scratch->end) {
+ scratch->curr = scratch->memory;
+ retval = scratch->curr;
+ }
+
+ // HACK!!!!!: Using size instead of some kind of "old size"
+ memcpy(retval, prev_memory, size);
+ } break;
+ }
+
+ return retval;
+}
+
+
+
+
+//-------------------------------------------------------------------------------------
+// CONVERSION FUNCTIONS IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+u8* uint_to_uleb128(u64 n, i32* output_length) {
+ static u8 buffer[16];
+
+ *output_length = 0;
+ u8* output = buffer;
+ u8 byte;
+ do {
+ byte = n & 0x7f;
+ n >>= 7;
+ if (n != 0) byte |= (1 << 7);
+ *output++ = byte;
+ (*output_length)++;
+ } while (n != 0);
+
+ return buffer;
+}
+
+
+// Converts a signed integer to the signed LEB128 format
+u8* int_to_leb128(i64 n, i32* output_length) {
+ static u8 buffer[16];
+
+ *output_length = 0;
+ u8* output = buffer;
+ b32 more = 1;
+
+ i32 size = 64;
+
+ u8 byte;
+ do {
+ byte = n & 0x7f;
+ n >>= 7;
+
+ more = !(((n == 0) && (byte & 0x40) == 0) || ((n == -1) && (byte & 0x40) != 0));
+ if (more)
+ byte |= 0x80;
+ *output++ = byte;
+ (*output_length)++;
+ } while (more);
+
+ return buffer;
+}
+
+// NOTE: This assumes the underlying implementation of float on the host
+// system is already IEEE-754. This is safe to assume in most cases.
+u8* float_to_ieee754(f32 f, b32 reverse) {
+ static u8 buffer[4];
+
+ u8* fmem = (u8*) &f;
+ if (reverse) {
+ buffer[0] = fmem[3];
+ buffer[1] = fmem[2];
+ buffer[2] = fmem[1];
+ buffer[3] = fmem[0];
+ } else {
+ buffer[0] = fmem[0];
+ buffer[1] = fmem[1];
+ buffer[2] = fmem[2];
+ buffer[3] = fmem[3];
+ }
+
+ return buffer;
+}
+
+u8* double_to_ieee754(f64 f, b32 reverse) {
+ static u8 buffer[8];
+
+ u8* fmem = (u8*) &f;
+ if (reverse) {
+ buffer[0] = fmem[7];
+ buffer[1] = fmem[6];
+ buffer[2] = fmem[5];
+ buffer[3] = fmem[4];
+ buffer[4] = fmem[3];
+ buffer[5] = fmem[2];
+ buffer[6] = fmem[1];
+ buffer[7] = fmem[0];
+ } else {
+ buffer[0] = fmem[0];
+ buffer[1] = fmem[1];
+ buffer[2] = fmem[2];
+ buffer[3] = fmem[3];
+ buffer[4] = fmem[4];
+ buffer[5] = fmem[5];
+ buffer[6] = fmem[6];
+ buffer[7] = fmem[7];
+ }
+
+ return buffer;
+}
+
+u64 uleb128_to_uint(u8* bytes, i32 *byte_count) {
+ u64 res = 0;
+ u64 shift = 0;
+
+ while (1) {
+ u8 byte = bytes[(*byte_count)++];
+ res |= (byte & 0x7f) << shift;
+ if ((byte & 0x80) == 0) break;
+ shift += 7;
+ }
+ return res;
+}
+
+
+
+//-------------------------------------------------------------------------------------
+// STRING IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+b32 bh_str_starts_with(char* str, char* start) {
+ char* s = str;
+ char* p = start;
+
+ while (*s != '\0' && *p != '\0' && *s == *p) s++, p++;
+
+ return *p == '\0';
+}
+
+b32 bh_str_ends_with(char* str, char* end) {
+ i32 slen = strlen(str);
+ i32 elen = strlen(end);
+
+ char* s = str + slen - 1;
+ char* e = end + elen - 1;
+
+ while (*e == *s && e != end && s != str) e--, s--;
+
+ return *e == *s;
+}
+
+char* bh_strdup(bh_allocator a, char* str) {
+ u32 len = strlen(str);
+ char* buf = bh_alloc(a, len + 1);
+
+ char* t = buf;
+ while ((*t++ = *str++));
+ return buf;
+}
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// FILE IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_FILE
+
+static b32 bh__file_seek_wrapper(bh_file_descriptor fd, i64 offset, bh_file_whence whence, i64* new_offset);
+
+bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand) {
+ const char* filename = NULL;
+
+#if defined(_BH_WINDOWS)
+ bh_file_descriptor sd_fd;
+
+ switch (stand) {
+ case BH_FILE_STANDARD_INPUT:
+ sd_fd = GetStdHandle(STD_INPUT_HANDLE);
+ filename = "stdin";
+ break;
+ case BH_FILE_STANDARD_OUTPUT:
+ sd_fd = GetStdHandle(STD_OUTPUT_HANDLE);
+ filename = "stdout";
+ break;
+ case BH_FILE_STANDARD_ERROR:
+ sd_fd = GetStdHandle(STD_ERROR_HANDLE);
+ filename = "stderr";
+ break;
+ default:
+ return BH_FILE_ERROR_BAD_FD;
+ }
+ file->fd = sd_fd;
+
+#elif defined(_BH_LINUX)
+ i32 sd_fd = -1;
+
+ switch (stand) {
+ case BH_FILE_STANDARD_INPUT:
+ sd_fd = STDIN_FILENO;
+ filename = "stdin"; // These are constants in the data section so everything should be okay
+ break;
+ case BH_FILE_STANDARD_OUTPUT:
+ sd_fd = STDOUT_FILENO;
+ filename = "stdout";
+ break;
+ case BH_FILE_STANDARD_ERROR:
+ sd_fd = STDERR_FILENO;
+ filename = "stderr";
+ break;
+ default:
+ return BH_FILE_ERROR_BAD_FD;
+ }
+
+ file->fd = sd_fd;
+#endif
+
+
+ file->filename = filename;
+ return BH_FILE_ERROR_NONE;
+}
+
+bh_file_error bh_file_create(bh_file* file, const char* filename) {
+ // Need to do this to avoid compiler complaining about types
+ bh_file_mode write_rw = (bh_file_mode) (BH_FILE_MODE_WRITE | BH_FILE_MODE_RW);
+ return bh_file_open_mode(file, write_rw, filename);
+}
+
+bh_file_error bh_file_open(bh_file* file, const char* filename) {
+ return bh_file_open_mode(file, BH_FILE_MODE_READ, filename);
+}
+
+bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename) {
+#if defined(_BH_WINDOWS)
+ DWORD desired_access;
+ DWORD creation_disposition;
+
+ switch (mode & BH_FILE_MODE_MODES) {
+ case BH_FILE_MODE_READ:
+ desired_access = GENERIC_READ;
+ creation_disposition = OPEN_EXISTING;
+ break;
+ case BH_FILE_MODE_WRITE:
+ desired_access = GENERIC_WRITE;
+ creation_disposition = CREATE_ALWAYS;
+ break;
+ case BH_FILE_MODE_APPEND:
+ desired_access = GENERIC_WRITE;
+ creation_disposition = OPEN_ALWAYS;
+ break;
+ case BH_FILE_MODE_READ | BH_FILE_MODE_RW:
+ desired_access = GENERIC_READ | GENERIC_WRITE;
+ creation_disposition = OPEN_EXISTING;
+ break;
+ case BH_FILE_MODE_WRITE | BH_FILE_MODE_RW:
+ desired_access = GENERIC_READ | GENERIC_WRITE;
+ creation_disposition = CREATE_ALWAYS;
+ break;
+ case BH_FILE_MODE_APPEND | BH_FILE_MODE_RW:
+ desired_access = GENERIC_READ | GENERIC_WRITE;
+ creation_disposition = OPEN_ALWAYS;
+ break;
+ default:
+ return BH_FILE_ERROR_INVALID;
+ }
+
+
+ file->fd = CreateFileA(filename,
+ desired_access,
+ FILE_SHARE_READ,
+ NULL,
+ creation_disposition,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (file->fd == INVALID_HANDLE_VALUE) {
+ return BH_FILE_ERROR_INVALID;
+ }
+
+ file->filename = filename;
+ return BH_FILE_ERROR_NONE;
+
+#elif defined(_BH_LINUX)
+ i32 os_mode = 0;
+
+ switch (mode & BH_FILE_MODE_MODES) {
+ case BH_FILE_MODE_READ: os_mode = O_RDONLY; break;
+ case BH_FILE_MODE_WRITE: os_mode = O_WRONLY | O_CREAT | O_TRUNC; break;
+ case BH_FILE_MODE_APPEND: os_mode = O_WRONLY | O_APPEND | O_CREAT; break;
+ case BH_FILE_MODE_READ | BH_FILE_MODE_RW: os_mode = O_RDWR; break;
+ case BH_FILE_MODE_WRITE | BH_FILE_MODE_RW: os_mode = O_RDWR | O_CREAT | O_TRUNC; break;
+ case BH_FILE_MODE_APPEND | BH_FILE_MODE_RW: os_mode = O_RDWR | O_APPEND | O_CREAT; break;
+ //default: // TODO Handle errors
+ }
+
+ file->fd = open(filename, os_mode,
+ S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP //+rw-rw-rw-
+ );
+ if (file->fd < 0) {
+ return BH_FILE_ERROR_INVALID;
+ }
+
+ // TODO: Set this using some allocator
+ file->filename = filename;
+
+ return BH_FILE_ERROR_NONE;
+#endif
+}
+
+bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename) {
+ file->filename = filename; // This may be unsafe
+ file->fd = fd;
+ return BH_FILE_ERROR_NONE;
+}
+
+b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read) {
+#if defined(_BH_WINDOWS)
+ bh_file_seek_to(file, offset);
+ BOOL res = ReadFile(file->fd, buffer, buff_size, (i32 *) bytes_read, NULL);
+ if (res) return 1;
+ else return 0;
+
+#elif defined(_BH_LINUX)
+ if (file->fd == 0) {
+ isize res = read(file->fd, buffer, buff_size);
+ if (res < 0) return 0;
+ if (bytes_read) *bytes_read = res;
+ return 1;
+ }
+
+ isize res = pread(file->fd, buffer, buff_size, offset);
+ if (res < 0) return 0;
+ if (bytes_read) *bytes_read = res;
+ return 1;
+#endif
+}
+
+b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote) {
+ isize res;
+ i64 current_offset = 0;
+ bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_CURRENT, ¤t_offset);
+
+#if defined(_BH_WINDOWS)
+ if (current_offset != offset) bh_file_seek_to(file, offset);
+ res = (isize) WriteFile(file->fd, buffer, buff_size, (i32 *) bytes_wrote, NULL);
+ return res;
+
+#elif defined(_BH_LINUX)
+ if (current_offset == offset || file->fd == 1 || file->fd == 2) {
+ // Standard in and out do like pwrite()
+ res = write(file->fd, buffer, buff_size);
+ } else {
+ res = pwrite(file->fd, buffer, buff_size, offset);
+ }
+ if (res < 0) return 0;
+ if (bytes_wrote) *bytes_wrote = res;
+
+ return 1;
+#endif
+}
+
+static b32 bh__file_seek_wrapper(bh_file_descriptor fd, i64 offset, bh_file_whence whence, i64* new_offset) {
+#if defined(_BH_WINDOWS)
+ LARGE_INTEGER new_file_pointer;
+ LARGE_INTEGER dest;
+ dest.QuadPart = offset;
+
+ BOOL res = SetFilePointerEx(fd, dest, &new_file_pointer, whence);
+ *new_offset = new_file_pointer.QuadPart;
+
+ return res;
+
+#elif defined(_BH_LINUX)
+ i64 res = lseek64(fd, offset, whence);
+ if (res < 0) return 0;
+ if (new_offset) *new_offset = res;
+ return 1;
+#endif
+}
+
+// Returns new offset
+i64 bh_file_seek(bh_file* file, i64 offset, bh_file_whence whence) {
+ i64 new_offset = -1;
+ bh__file_seek_wrapper(file->fd, offset, whence, &new_offset);
+ return new_offset;
+}
+
+i64 bh_file_seek_to(bh_file* file, i64 offset) {
+ i64 new_offset = -1;
+ bh__file_seek_wrapper(file->fd, offset, BH_FILE_WHENCE_BEGIN, &new_offset);
+ return new_offset;
+}
+
+i64 bh_file_seek_to_end(bh_file* file) {
+ i64 new_offset = -1;
+ bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_END, &new_offset);
+ return new_offset;
+}
+
+i64 bh_file_skip(bh_file* file, i64 bytes) {
+ i64 new_offset = 0;
+ bh__file_seek_wrapper(file->fd, bytes, BH_FILE_WHENCE_CURRENT, &new_offset);
+ return new_offset;
+}
+
+i64 bh_file_tell(bh_file* file) {
+ i64 new_offset = 0;
+ bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_CURRENT, &new_offset);
+ return new_offset;
+}
+
+bh_file_error bh_file_close(bh_file* file) {
+ bh_file_error err = BH_FILE_ERROR_NONE;
+
+#if defined(_BH_WINDOWS)
+ BOOL success = CloseHandle(file->fd);
+ if (!success) err = BH_FILE_ERROR_INVALID;
+
+ return err;
+
+#elif defined(_BH_LINUX)
+ i32 res = close(file->fd);
+ if (res < 0)
+ err = BH_FILE_ERROR_INVALID;
+
+ return err;
+#endif
+}
+
+b32 bh_file_read(bh_file* file, void* buffer, isize buff_size) {
+ return bh_file_read_at(file, bh_file_tell(file), buffer, buff_size, NULL);
+}
+
+b32 bh_file_write(bh_file* file, void* buffer, isize buff_size) {
+ return bh_file_write_at(file, bh_file_tell(file), buffer, buff_size, NULL);
+}
+
+void bh_file_flush(bh_file* file) {
+ #ifdef _BH_LINUX
+ fdatasync(file->fd);
+ #endif
+}
+
+i64 bh_file_size(bh_file* file) {
+ i64 size = 0;
+ i64 prev = bh_file_tell(file);
+ bh_file_seek_to_end(file);
+ size = bh_file_tell(file);
+ bh_file_seek_to(file, prev);
+ return size;
+}
+
+bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file) {
+ bh_file_contents fc = {
+ .allocator = alloc,
+ .filename = bh_strdup(alloc, (char *) file->filename),
+ .length = 0, .data = NULL, .line_count = 0,
+ };
+
+ isize size = bh_file_size(file);
+ if (size <= 0) return fc;
+
+ fc.data = bh_alloc(alloc, size + 1);
+ fc.length = size;
+ bh_file_read_at(file, 0, fc.data, fc.length, NULL);
+ ((u8*) fc.data)[fc.length] = '\0';
+
+ return fc;
+}
+
+bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename) {
+ bh_file file;
+ bh_file_open(&file, filename);
+ bh_file_contents fc = bh_file_read_contents(alloc, &file);
+ bh_file_close(&file);
+ return fc;
+}
+
+b32 bh_file_contents_free(bh_file_contents* contents) {
+ bh_free(contents->allocator, contents->data);
+ contents->length = 0;
+ return 1;
+}
+
+b32 bh_file_stat(char const* filename, bh_file_stats* out) {
+ struct stat s;
+ if (stat(filename, &s) == -1) {
+ return 0;
+ }
+
+ out->size = s.st_size;
+
+ if ((s.st_mode & S_IFMT) == S_IFDIR) out->file_type = BH_FILE_TYPE_DIRECTORY;
+ if ((s.st_mode & S_IFMT) == S_IFREG) out->file_type = BH_FILE_TYPE_FILE;
+
+#if defined(_BH_LINUX)
+ if ((s.st_mode & S_IFMT) == S_IFLNK) out->file_type = BH_FILE_TYPE_LINK;
+#endif
+
+ return 1;
+}
+
+b32 bh_file_exists(char const* filename) {
+ struct stat s;
+ return stat(filename, &s) != -1;
+}
+
+b32 bh_file_remove(char const* filename) {
+#if defined(_BH_WINDOWS)
+ return DeleteFileA(filename);
+
+#elif defined(_BH_LINUX)
+ return unlink(filename) == 0;
+#endif
+}
+
+char* bh_path_get_full_name(char const* filename, bh_allocator a) {
+#if defined(_BH_WINDOWS)
+ char buffer[4096];
+ GetFullPathNameA(filename, 4096, buffer, NULL);
+
+ i32 len = strlen(buffer);
+ char* result = bh_alloc_array(a, char, len + 1);
+ memmove(result, buffer, len);
+ result[len] = 0;
+
+ return result;
+
+#elif defined(_BH_LINUX)
+ char* p = realpath(filename, NULL);
+
+ // Check if the file did not exists.
+ // :Cleanup should this return NULL?
+ if (p == NULL) return (char *) filename;
+
+ i32 len = strlen(p);
+ char* result = bh_alloc_array(a, char, len + 1);
+ memmove(result, p, len);
+ result[len] = 0;
+
+ free(p);
+
+ return result;
+#endif
+}
+
+// NOTE: This assumes the filename is the full path, not relative to anything else.
+#if defined(_BH_LINUX)
+ #define DIR_SEPARATOR '/'
+#elif defined(_BH_WINDOWS)
+ #define DIR_SEPARATOR '\\'
+#endif
+char* bh_path_get_parent(char const* filename, bh_allocator a) {
+
+ char* result = bh_strdup(a, (char *) filename);
+ char* end = result + strlen(result);
+ while (*end != DIR_SEPARATOR && end != result) *end-- = '\0';
+
+ return result;
+}
+
+// This function returns a volatile pointer. Do not store it without copying!
+char* bh_lookup_file(char* filename, char* relative_to, char *suffix, b32 add_suffix, bh_arr(const char *) included_folders, b32 search_included_folders) {
+ assert(relative_to != NULL);
+
+ static char path[512];
+ fori (i, 0, 512) path[i] = 0;
+
+ static char fn[256];
+ fori (i, 0, 256) fn[i] = 0;
+
+ if (!bh_str_ends_with(filename, suffix) && add_suffix) {
+ bh_snprintf(fn, 256, "%s%s", filename, suffix);
+ } else {
+ bh_snprintf(fn, 256, "%s", filename);
+ }
+
+ fori (i, 0, 256) 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, 512, "%s%c%s", relative_to, DIR_SEPARATOR, fn + 2);
+ else
+ bh_snprintf(path, 512, "%s%s", relative_to, fn + 2);
+
+ if (bh_file_exists(path)) return bh_path_get_full_name(path, bh_heap_allocator());
+
+ return fn;
+ }
+
+ if (search_included_folders) {
+ bh_arr_each(const char *, folder, included_folders) {
+ if ((*folder)[strlen(*folder) - 1] != DIR_SEPARATOR)
+ bh_snprintf(path, 512, "%s%c%s", *folder, DIR_SEPARATOR, fn);
+ else
+ bh_snprintf(path, 512, "%s%s", *folder, fn);
+
+ if (bh_file_exists(path)) return bh_path_get_full_name(path, bh_heap_allocator());
+ }
+ }
+
+ return fn;
+}
+
+//
+// Modifies the path in-place.
+char* bh_path_convert_separators(char* path) {
+#if defined(_BH_LINUX)
+ #define DIR_SEPARATOR '/'
+ #define OTHER_SEPARATOR '\\'
+#elif defined(_BH_WINDOWS)
+ #define DIR_SEPARATOR '\\'
+ #define OTHER_SEPARATOR '/'
+#endif
+
+ fori (i, 0, (i64) strlen(path)) {
+ if (path[i] == OTHER_SEPARATOR) {
+ path[i] = DIR_SEPARATOR;
+ }
+ }
+
+ return path;
+}
+
+
+bh_dir bh_dir_open(char* path) {
+#ifdef _BH_WINDOWS
+ char new_path[512] = { 0 };
+ strncpy(new_path, path, 511);
+ bh_path_convert_separators(new_path);
+ strncat(new_path, "\\*.*", 511);
+
+ Windows_Directory_Opened* dir = malloc(sizeof(Windows_Directory_Opened));
+ dir->hndl = FindFirstFileA(new_path, &dir->found_file);
+ if (dir->hndl == INVALID_HANDLE_VALUE) {
+ return NULL;
+ }
+
+ return dir;
+#endif
+
+#ifdef _BH_LINUX
+ DIR* dir = opendir(path);
+ return dir;
+#endif
+}
+
+b32 bh_dir_read(bh_dir dir, bh_dirent* out) {
+
+#ifdef _BH_WINDOWS
+ do {
+ BOOL success = FindNextFileA(dir->hndl, &dir->found_file);
+ if (!success) return 0;
+ } while (!strcmp(dir->found_file.cFileName, ".") || !strcmp(dir->found_file.cFileName, ".."));
+
+ if (out == NULL) return 1;
+
+ out->type = (dir->found_file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ ? BH_DIRENT_DIRECTORY : BH_DIRENT_FILE;
+ out->id = 0;
+ out->name_length = strlen(dir->found_file.cFileName);
+ strncpy(out->name, dir->found_file.cFileName, 256);
+
+ return 1;
+#endif
+
+#ifdef _BH_LINUX
+ struct dirent *ent;
+ while (1) {
+ ent = readdir(dir);
+ if (ent == NULL) return 0;
+
+ // Skip the current directory and parent directory
+ if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) break;
+ }
+
+ bh_dirent_type type = 0;
+ switch (ent->d_type) {
+ case DT_UNKNOWN: break;
+ case DT_BLK: type = BH_DIRENT_BLOCK; break;
+ case DT_CHR: type = BH_DIRENT_CHAR; break;
+ case DT_DIR: type = BH_DIRENT_DIRECTORY; break;
+ case DT_LNK: type = BH_DIRENT_SYMLINK; break;
+ case DT_REG: type = BH_DIRENT_FILE; break;
+ default: type = BH_DIRENT_OTHER; break;
+ }
+
+ if (out == NULL) return 1;
+
+ out->type = type;
+ out->id = (u32) ent->d_ino;
+ out->name_length = strlen(ent->d_name);
+ strncpy(out->name, ent->d_name, 256);
+
+ return 1;
+#endif
+}
+
+void bh_dir_close(bh_dir dir) {
+#ifdef _BH_WINDOWS
+ if (dir == NULL) return;
+
+ FindClose(dir->hndl);
+ free(dir);
+#endif
+
+#ifdef _BH_LINUX
+ if (dir == NULL) return;
+ closedir(dir);
+#endif
+}
+
+#undef DIR_SEPARATOR
+
+#endif // ifndef BH_NO_FILE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// ALTERNATE PRINTF IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+isize bh_printf(char const *fmt, ...) {
+ isize res;
+ va_list va;
+ va_start(va, fmt);
+ res = bh_printf_va(fmt, va);
+ va_end(va);
+ return res;
+}
+
+isize bh_printf_va(char const *fmt, va_list va) {
+ bh_file file;
+ bh_file_get_standard(&file, BH_FILE_STANDARD_OUTPUT);
+ return bh_fprintf_va(&file, fmt, va);
+}
+
+isize bh_printf_err(char const *fmt, ...) {
+ isize res;
+ va_list va;
+ va_start(va, fmt);
+ res = bh_printf_err_va(fmt, va);
+ va_end(va);
+ return res;
+}
+
+isize bh_printf_err_va(char const *fmt, va_list va) {
+ bh_file file;
+ bh_file_get_standard(&file, BH_FILE_STANDARD_ERROR);
+ return bh_fprintf_va(&file, fmt, va);
+}
+
+isize bh_fprintf(bh_file* f, char const *fmt, ...) {
+ isize res;
+ va_list va;
+ va_start(va, fmt);
+ res = bh_fprintf_va(f, fmt, va);
+ va_end(va);
+ return res;
+}
+
+isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va) {
+ static char buffer[4096];
+ isize len = bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
+ bh_file_write(f, buffer, len - 1);
+ return len;
+}
+
+char* bh_bprintf(char const *fmt, ...) {
+ char* res;
+ va_list va;
+ va_start(va, fmt);
+ res = bh_bprintf_va(fmt, va);
+ va_end(va);
+ return res;
+}
+
+char* bh_bprintf_va(char const *fmt, va_list va) {
+ static char buffer[4096];
+ isize len = bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
+ buffer[len - 1] = 0;
+ return buffer;
+}
+
+char* bh_aprintf(bh_allocator alloc, const char* fmt, ...) {
+ char* res;
+ va_list va;
+ va_start(va, fmt);
+ res = bh_aprintf_va(alloc, fmt, va);
+ va_end(va);
+ return res;
+}
+
+char* bh_aprintf_va(bh_allocator alloc, const char* fmt, va_list va) {
+ static char buffer[4096];
+ isize len = bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
+ char* res = bh_alloc(alloc, len);
+ memcpy(res, buffer, len);
+ res[len - 1] = 0;
+ return res;
+}
+
+isize bh_snprintf(char *str, isize n, char const *fmt, ...) {
+ isize res;
+ va_list va;
+ va_start(va, fmt);
+ res = bh_snprintf_va(str, n, fmt, va);
+ va_end(va);
+ return res;
+}
+
+isize bh__print_string(char* dest, isize n, char* src) {
+ isize len = 0;
+ while (n-- && (*dest++ = *src++)) len++;
+ return len;
+}
+
+isize bh__printu64(char* str, isize n, bh__print_format format, u64 value) {
+ char buf[128];
+ buf[127] = 0;
+ char* walker = buf + 127;
+ u32 base = format.base ? format.base : 10, tmp;
+
+ while (value > 0) {
+ tmp = value % base;
+ if (tmp > 9) {
+ switch (tmp) {
+ case 10: tmp = 'a'; break;
+ case 11: tmp = 'b'; break;
+ case 12: tmp = 'c'; break;
+ case 13: tmp = 'd'; break;
+ case 14: tmp = 'e'; break;
+ case 15: tmp = 'f'; break;
+ }
+ } else {
+ tmp += '0';
+ }
+
+ *--walker = tmp;
+ value /= base;
+ }
+
+ if (format.base == 16) {
+ *--walker = 'x';
+ *--walker = '0';
+ }
+
+ return bh__print_string(str, n, walker);
+}
+
+isize bh__printi64(char* str, isize n, bh__print_format format, i64 value) {
+ char buf[128];
+ buf[127] = 0;
+ char* walker = buf + 127;
+ u32 base = format.base ? format.base : 10, tmp;
+
+ b32 negative = value < 0 ? 1 : 0;
+ if (negative) value = -value;
+
+ if (value == 0) {
+ *--walker = '0';
+ } else {
+ while (value > 0) {
+ tmp = value % base;
+ if (tmp > 9) {
+ switch (tmp) {
+ case 10: tmp = 'a'; break;
+ case 11: tmp = 'b'; break;
+ case 12: tmp = 'c'; break;
+ case 13: tmp = 'd'; break;
+ case 14: tmp = 'e'; break;
+ case 15: tmp = 'f'; break;
+ }
+ } else {
+ tmp += '0';
+ }
+
+ *--walker = tmp;
+ value /= base;
+ }
+ }
+
+ if (negative) {
+ *--walker = '-';
+ }
+
+ if (format.base == 16) {
+ *--walker = 'x';
+ *--walker = '0';
+ }
+
+ return bh__print_string(str, n, walker);
+}
+
+// TODO: This implementation is VERY VERY BAD AND WRONG. Fix it.
+isize bh__printf64(char* str, isize n, f64 value) {
+ fori (i, 0, 6) value *= 10.0;
+ i64 v = (i64) value;
+
+ isize l1 = bh__printi64(str, n, ((bh__print_format) { .base = 10 }), v / 1000000);
+ str += l1;
+ n -= l1;
+
+ *str = '.';
+ str += 1;
+ n -= 1;
+
+ isize l2 = bh__printi64(str, n, ((bh__print_format) { .base = 10 }), bh_abs(v) % 1000000);
+
+ return l1 + l2 + 1;
+}
+
+// TODO: This is very hacked together but for now it will work.
+isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va) {
+ char const *text_start = str;
+ isize res;
+
+ while (*fmt) {
+ bh__print_format format = { 0 };
+ isize len = 0;
+
+ while (*fmt && *fmt != '%') {
+ *(str++) = *(fmt++);
+ }
+
+ if (!*fmt) goto end_of_format;
+
+ fmt++;
+
+ switch (*fmt++) {
+ case 'o': format.base = 8; break;
+ case 'x': format.base = 16; break;
+ default: fmt--;
+ }
+
+ switch (*fmt) {
+ case 'c': {
+ char c = (char) va_arg(va, int);
+ *(str++) = c;
+ } break;
+
+ case 'd': {
+ i64 value = (i64) va_arg(va, int);
+ len = bh__printi64(str, n, format, value);
+ } break;
+
+ case 'l': {
+ i64 value = (i64) va_arg(va, long);
+ len = bh__printi64(str, n, format, value);
+ } break;
+
+ case 'p': {
+ u64 value = (u64) va_arg(va, ptr);
+ format.base = 16;
+ len = bh__printu64(str, n, format, value);
+ } break;
+
+ case 's': {
+ char* s = va_arg(va, char *);
+ len = bh__print_string(str, n, s);
+ } break;
+
+ case 'b': { // String with a length (not null terminated)
+ char* s = va_arg(va, char *);
+ i32 l = va_arg(va, int);
+ len = bh__print_string(str, bh_min(l, n), s);
+ } break;
+
+ case 'f': {
+ f64 f = va_arg(va, f64);
+ len = bh__printf64(str, n, f);
+ } break;
+
+ default: fmt--;
+ }
+
+ fmt++;
+
+end_of_format:
+ str += len;
+ n -= len;
+ }
+
+ return str - text_start + 1;
+}
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// FLEXIBLE BUFFER IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_BUFFER
+
+void bh_buffer_init(bh_buffer* buffer, bh_allocator alloc, i32 init_size) {
+ buffer->allocator = alloc;
+ buffer->length = 0;
+ buffer->capacity = init_size;
+ buffer->data = bh_alloc(alloc, init_size);
+}
+
+void bh_buffer_free(bh_buffer* buffer) {
+ bh_free(buffer->allocator, buffer->data);
+ buffer->length = 0;
+ buffer->capacity = 0;
+}
+
+void bh_buffer_clear(bh_buffer* buffer) {
+ buffer->length = 0;
+}
+
+void bh_buffer_grow(bh_buffer* buffer, i32 length) {
+ if (buffer == NULL) return;
+
+ if (buffer->capacity >= length) {
+ // NOTE: Already have enough room
+ return;
+ }
+
+ i32 newcap = buffer->capacity;
+ while (newcap < length) newcap = BH_BUFFER_GROW_FORMULA(newcap);
+
+ ptr new_data = bh_resize(buffer->allocator, buffer->data, newcap);
+ if (new_data == NULL) return;
+
+ buffer->capacity = newcap;
+ buffer->data = new_data;
+}
+
+void bh_buffer_append(bh_buffer* buffer, const void * data, i32 length) {
+ if (buffer == NULL) return;
+
+ if (buffer->length + length > buffer->capacity) {
+ bh_buffer_grow(buffer, buffer->length + length);
+ }
+
+ memcpy(buffer->data + buffer->length, data, length);
+ buffer->length += length;
+}
+
+void bh_buffer_concat(bh_buffer* buffer, bh_buffer other) {
+ bh_buffer_append(buffer, other.data, other.length);
+}
+
+void bh_buffer_write_byte(bh_buffer* buffer, u8 byte) {
+ bh_buffer_grow(buffer, buffer->length + 1);
+ buffer->data[buffer->length++] = byte;
+}
+
+void bh_buffer_write_u32(bh_buffer* buffer, u32 i) {
+ bh_buffer_grow(buffer, buffer->length + 4);
+ *((u32 *) bh_pointer_add(buffer->data, buffer->length)) = i;
+ buffer->length += 4;
+}
+
+void bh_buffer_write_u64(bh_buffer* buffer, u64 i) {
+ bh_buffer_grow(buffer, buffer->length + 8);
+ *((u64 *) bh_pointer_add(buffer->data, buffer->length)) = i;
+ buffer->length += 8;
+}
+
+void bh_buffer_align(bh_buffer* buffer, u32 alignment) {
+ if (buffer->length % alignment != 0) {
+ u32 difference = alignment - (buffer->length % alignment);
+ buffer->length += difference;
+
+ bh_buffer_grow(buffer, buffer->length);
+ }
+}
+
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// ARRAY IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_ARRAY
+
+b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap) {
+ bh__arr* arrptr;
+
+ if (*arr == NULL) {
+ if (cap == 0 && elemsize == 0) return 1;
+
+ arrptr = (bh__arr *) bh_alloc(alloc, sizeof(*arrptr) + elemsize * cap);
+ if (arrptr == NULL) return 0;
+
+ arrptr->allocator = alloc;
+ arrptr->capacity = cap;
+ arrptr->length = 0;
+
+ } else {
+ arrptr = bh__arrhead(*arr);
+
+ if (arrptr->capacity < cap) {
+ void* p;
+ i32 newcap = arrptr->capacity;
+ while (newcap < cap) newcap = BH_ARR_GROW_FORMULA(newcap);
+
+ p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * newcap);
+
+ if (p) {
+ arrptr = (bh__arr *) p;
+ arrptr->capacity = newcap;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ *arr = arrptr + 1;
+ return 1;
+}
+
+b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap) {
+ if (*arr == NULL) return 0;
+
+ bh__arr* arrptr = bh__arrhead(*arr);
+ cap = bh_max(cap, arrptr->length);
+
+ if (arrptr->capacity > cap) {
+ void* p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * cap);
+
+ if (p) {
+ arrptr = (bh__arr *) p;
+ arrptr->capacity = cap;
+ } else {
+ return 0;
+ }
+ }
+
+ *arr = arrptr + 1;
+ return 1;
+}
+
+b32 bh__arr_free(void **arr) {
+ if (*arr == NULL) return 0;
+
+ bh__arr* arrptr = bh__arrhead(*arr);
+ bh_free(arrptr->allocator, arrptr);
+ *arr = NULL;
+ return 1;
+}
+
+void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize) {
+ bh__arr* arrptr = bh__arrhead(arr);
+
+ const i32 cap = arrptr->length;
+
+ void* newarr = NULL;
+ bh__arr_grow(alloc, &newarr, elemsize, cap);
+ bh__arrhead(newarr)->length = cap;
+ bh__arrhead(newarr)->capacity = cap;
+ memcpy(newarr, arr, elemsize * arrptr->length);
+
+ return newarr;
+}
+
+void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems) {
+ bh__arr* arrptr = bh__arrhead(*arr);
+
+ if (index >= arrptr->length) return; // Can't delete past the end of the array
+ if (numelems <= 0) return; // Can't delete nothing
+
+ memmove(
+ (char *)(*arr) + elemsize * index, // Target
+ (char *)(*arr) + elemsize * (index + numelems), // Source
+ elemsize * (arrptr->length - (index + numelems))); // Length
+ arrptr->length -= numelems;
+}
+
+void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems) {
+ if (numelems) {
+ if (*arr == NULL) {
+ bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, numelems); // Making a new array
+ return;
+ }
+
+ bh__arr* arrptr = bh__arrhead(*arr);
+ if (!bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, arrptr->length + numelems)) return; // Fail case
+ arrptr = bh__arrhead(*arr);
+ memmove(
+ (char *)(*arr) + elemsize * (index + numelems),
+ (char *)(*arr) + elemsize * index,
+ elemsize * (arrptr->length - index));
+ arrptr->length += numelems;
+ }
+}
+
+#endif // ifndef BH_NO_ARRAY
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// TABLE IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_TABLE
+
+b32 bh__table_init(bh_allocator allocator, bh__table **table, i32 table_size) {
+ *table = bh_alloc(allocator, sizeof(bh__table) + sizeof(ptr) * table_size);
+ if (*table == NULL) return 0;
+
+ (*table)->allocator = allocator;
+ (*table)->table_size = table_size;
+
+ for (i32 i = 0; i < table_size; i++) {
+ (*table)->arrs[i] = NULL;
+ }
+
+ return 1;
+}
+
+b32 bh__table_free(bh__table **table) {
+ if (*table == NULL) return 0;
+
+ for (u64 i = 0; i < (*table)->table_size; i++) {
+ if ((*table)->arrs[i] != NULL) {
+ bh_arr_free((*table)->arrs[i]);
+ }
+ }
+
+ bh_free((*table)->allocator, *table);
+ *table = NULL;
+ return 1;
+}
+
+// Assumes NULL terminated string for key
+ptr bh__table_put(bh__table *table, i32 elemsize, char *key) {
+ elemsize += (elemsize & 1);
+
+ u64 index = bh__table_hash_function(key, 0, table->table_size);
+ u8 arr_was_new = 0;
+
+ ptr arrptr = table->arrs[index];
+ if (arrptr == NULL) {
+ arr_was_new = 1;
+ goto add_new_element;
+ }
+ u64 len = *(u64 *) arrptr;
+ arrptr = bh_pointer_add(arrptr, sizeof(u64));
+
+ u16 key_length = 0;
+ while (len--) {
+ arrptr = bh_pointer_add(arrptr, elemsize);
+ key_length = *(u16 *) arrptr;
+ arrptr = bh_pointer_add(arrptr, sizeof(u16));
+ if (strncmp(key, (char *) arrptr, key_length) == 0) goto found_matching;
+ arrptr = bh_pointer_add(arrptr, key_length);
+ }
+
+add_new_element:
+ arrptr = table->arrs[index];
+ i32 byte_len = bh_arr_length(arrptr);
+ if (byte_len == 0) byte_len = sizeof(u64);
+ key_length = strlen(key) + 1;
+
+ // NOTE: Align to 16 bytes
+ if ((key_length + 2) % 16 != 0) {
+ key_length = ((((key_length + 2) >> 4) + 1) << 4) - 2;
+ }
+
+ bh__arr_grow(table->allocator, &arrptr, 1, byte_len + elemsize + sizeof(u16) + key_length);
+ bh__arrhead(arrptr)->length = byte_len + elemsize + sizeof(u16) + key_length;
+ table->arrs[index] = arrptr;
+
+ if (arr_was_new) {
+ *(u64 *) arrptr = 1;
+ } else {
+ (*(u64 *) arrptr)++;
+ }
+
+ arrptr = bh_pointer_add(arrptr, byte_len + elemsize);
+ *(u16 *) arrptr = key_length;
+ arrptr = bh_pointer_add(arrptr, sizeof(u16));
+ strncpy(arrptr, key, key_length);
+
+found_matching:
+ return bh_pointer_add(arrptr, -(sizeof(u16) + elemsize));
+}
+
+b32 bh__table_has(bh__table *table, i32 elemsize, char *key) {
+ elemsize += (elemsize & 1);
+
+ u64 index = bh__table_hash_function(key, 0, table->table_size);
+
+ ptr arrptr = table->arrs[index];
+ if (arrptr == NULL) return 0;
+
+ u64 len = *(u64 *) arrptr;
+ arrptr = bh_pointer_add(arrptr, sizeof(u64));
+
+ u16 key_length = 0;
+ while (len--) {
+ arrptr = bh_pointer_add(arrptr, elemsize);
+ key_length = *(u16 *) arrptr;
+ arrptr = bh_pointer_add(arrptr, sizeof(u16));
+ if (strncmp(key, (char *) arrptr, key_length) == 0) return 1;
+ arrptr = bh_pointer_add(arrptr, key_length);
+ }
+
+ return 0;
+}
+
+ptr bh__table_get(bh__table *table, i32 elemsize, char *key) {
+ elemsize += (elemsize & 1);
+
+ u64 index = bh__table_hash_function(key, 0, table->table_size);
+
+ ptr arrptr = table->arrs[index];
+ if (arrptr == NULL) return 0;
+
+ u64 len = *(u64 *) arrptr;
+ arrptr = bh_pointer_add(arrptr, sizeof(u64));
+
+ u16 key_length = 0;
+ while (len--) {
+ arrptr = bh_pointer_add(arrptr, elemsize);
+ key_length = *(u16 *) arrptr;
+ arrptr = bh_pointer_add(arrptr, sizeof(u16));
+ if (strncmp(key, (char *) arrptr, key_length) == 0) {
+ return bh_pointer_add(arrptr, -(sizeof(u16) + elemsize));
+ }
+ arrptr = bh_pointer_add(arrptr, key_length);
+ }
+
+ return NULL;
+}
+
+void bh__table_delete(bh__table *table, i32 elemsize, char *key) {
+ elemsize += (elemsize & 1);
+
+ u64 index = bh__table_hash_function(key, 0, table->table_size);
+
+ ptr arrptr = table->arrs[index], walker;
+ if (arrptr == NULL) return; // Didn't exist
+ walker = arrptr;
+
+ i32 byte_offset = 8;
+ i32 delete_len = 0;
+
+ u64 len = *(u64 *) walker;
+ walker = bh_pointer_add(walker, sizeof(u64));
+
+ u16 key_length = 0;
+ while (len--) {
+ walker = bh_pointer_add(walker, elemsize);
+ key_length = *(u16 *) walker;
+ walker = bh_pointer_add(walker, sizeof(u16));
+ if (strncmp(key, (char *) walker, key_length) == 0) {
+ delete_len = elemsize + sizeof(u16) + key_length;
+ goto found_matching;
+ }
+ walker = bh_pointer_add(walker, key_length);
+ byte_offset += elemsize + sizeof(u16) + key_length;
+ }
+
+ // NOTE: Already didn't exist
+ return;
+
+found_matching:
+ bh__arr_deleten((void **) &arrptr, 1, byte_offset, delete_len);
+ table->arrs[index] = arrptr;
+ (*(u64 *) arrptr)--;
+}
+
+void bh__table_clear(bh__table *table) {
+ for (u64 i = 0; i < table->table_size; i++) {
+ if (table->arrs[i] != NULL) {
+ // NOTE: Set length property to 0
+ *((u64 *) table->arrs[i]) = 0;
+ bh_arr_set_length(table->arrs[i], 0);
+ }
+ }
+}
+
+bh_table_iterator bh__table_iter_setup(bh__table *table, i32 elemsize) {
+ elemsize += (elemsize & 1);
+
+ bh_table_iterator it = {
+ .tab = table->arrs,
+ .endtab = table->arrs + table->table_size,
+ .elemsize = elemsize,
+ .entry = NULL
+ };
+ return it;
+}
+
+b32 bh_table_iter_next(bh_table_iterator* it) {
+ if (it->tab == NULL) return 0;
+
+ if (it->entry != NULL) {
+ it->arrlen--;
+ if (it->arrlen <= 0) {
+ it->tab++;
+ goto step_to_next;
+ }
+
+ it->entry = bh_pointer_add(it->entry, it->elemsize);
+ it->entry = bh_pointer_add(it->entry, sizeof(u16) + (*(u16 *) it->entry));
+ return 1;
+ }
+
+step_to_next:
+ // Step forward to find next valid
+ while (it->tab != it->endtab && *it->tab == NULL) {
+ it->tab++;
+ }
+
+ if (it->tab == it->endtab) return 0;
+
+ it->entry = *it->tab;
+ it->arrlen = *(u64 *) it->entry;
+ it->entry = bh_pointer_add(it->entry, sizeof(u64));
+ if (it->arrlen <= 0) {
+ it->tab++;
+ goto step_to_next;
+ }
+ return 1;
+}
+
+#endif // ifndef BH_NO_HASHTABLE
+
+
+
+//-------------------------------------------------------------------------------------
+// IMAP IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_IMAP
+void bh_imap_init(bh_imap* imap, bh_allocator alloc, i32 hash_count) {
+ imap->allocator = alloc;
+
+ imap->hashes = NULL;
+ imap->entries = NULL;
+
+ bh_arr_new(alloc, imap->hashes, hash_count);
+ bh_arr_new(alloc, imap->entries, 4);
+
+ fori(count, 0, hash_count) bh_arr_push(imap->hashes, -1);
+}
+
+void bh_imap_free(bh_imap* imap) {
+ bh_arr_free(imap->hashes);
+ bh_arr_free(imap->entries);
+
+ imap->hashes = NULL;
+ imap->entries = NULL;
+}
+
+bh__imap_lookup_result bh__imap_lookup(bh_imap* imap, bh_imap_entry_t key) {
+ bh__imap_lookup_result lr = { -1, -1, -1 };
+
+ u64 hash = 0xcbf29ce484222325ull ^ key;
+ u64 n = bh_arr_capacity(imap->hashes);
+
+ lr.hash_index = hash % n;
+ lr.entry_index = imap->hashes[lr.hash_index];
+ while (lr.entry_index >= 0) {
+ if (imap->entries[lr.entry_index].key == key) {
+ return lr;
+ }
+
+ lr.entry_prev = lr.entry_index;
+ lr.entry_index = imap->entries[lr.entry_index].next;
+ }
+
+ return lr;
+}
+
+void bh_imap_put(bh_imap* imap, bh_imap_entry_t key, bh_imap_entry_t value) {
+ bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
+
+ if (lr.entry_index >= 0) {
+ imap->entries[lr.entry_index].value = value;
+ return;
+ }
+
+ bh__imap_entry entry;
+ entry.key = key;
+ entry.value = value;
+ entry.next = imap->hashes[lr.hash_index];
+ bh_arr_push(imap->entries, entry);
+
+ imap->hashes[lr.hash_index] = bh_arr_length(imap->entries) - 1;
+}
+
+b32 bh_imap_has(bh_imap* imap, bh_imap_entry_t key) {
+ bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
+ return lr.entry_index >= 0;
+}
+
+bh_imap_entry_t bh_imap_get(bh_imap* imap, bh_imap_entry_t key) {
+ bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
+ if (lr.entry_index >= 0) {
+ return imap->entries[lr.entry_index].value;
+ } else {
+ return 0;
+ }
+}
+
+void bh_imap_delete(bh_imap* imap, bh_imap_entry_t key) {
+ bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
+ if (lr.entry_index < 0) return;
+
+ if (lr.entry_prev < 0) {
+ imap->hashes[lr.hash_index] = imap->entries[lr.entry_index].next;
+ } else {
+ imap->entries[lr.entry_prev].next = imap->entries[lr.entry_index].next;
+ }
+
+ // If it's that last thing in the array, just pop off the end
+ if (lr.entry_index == bh_arr_length(imap->entries) - 1) {
+ bh_arr_pop(imap->entries);
+ return;
+ }
+
+ bh_arr_fastdelete(imap->entries, lr.entry_index);
+ bh__imap_lookup_result last = bh__imap_lookup(imap, imap->entries[lr.entry_index].key);
+ if (last.entry_prev >= 0) {
+ imap->entries[last.entry_prev].next = lr.entry_index;
+ } else {
+ imap->hashes[last.hash_index] = lr.entry_index;
+ }
+}
+
+void bh_imap_clear(bh_imap* imap) {
+ // NOTE: Does not clear out an of the data that was in the map
+ bh_arr_each(i64, hash, imap->hashes) *hash = -1;
+ bh_arr_set_length(imap->entries, 0);
+}
+
+#endif // ifndef BH_NO_IMAP
+
+
+
+
+
+u64 bh_time_curr() {
+#if defined(_BH_WINDOWS)
+ LARGE_INTEGER result;
+ QueryPerformanceCounter(&result);
+ return (u64) result.QuadPart;
+
+#elif defined(_BH_LINUX)
+ struct timespec spec;
+ clock_gettime(CLOCK_REALTIME, &spec);
+
+ time_t sec = spec.tv_sec;
+ u64 ms = spec.tv_nsec / 1000000;
+ if (ms > 999) {
+ sec++;
+ ms = 0;
+ }
+
+ return sec * 1000 + ms;
+#endif
+}
+
+u64 bh_time_duration(u64 old) {
+#if defined(_BH_WINDOWS)
+ u64 curr = bh_time_curr();
+ u64 duration = curr - old;
+
+ LARGE_INTEGER freq;
+ QueryPerformanceFrequency(&freq);
+ duration *= 1000;
+ duration /= freq.QuadPart;
+ return duration;
+
+#elif defined(_BH_LINUX)
+ u64 curr = bh_time_curr();
+ return curr - old;
+#endif
+}
+
+#endif // ifdef BH_DEFINE
+
+#endif // ifndef BH_H
--- /dev/null
+
+#include "wasm.h"
+
+#if defined(_WIN32) || defined(_WIN64)
+ #define ONYX_EXPORT extern __declspec(dllexport)
+ #define ONYX_IMPORT extern __declspec(dllimport)
+#endif
+
+#if defined(__unix__)
+ #define ONYX_EXPORT
+ #define ONYX_IMPORT
+#endif
+
+typedef struct OnyxRuntime {
+ wasm_instance_t* wasm_instance;
+ wasm_module_t* wasm_module;
+ wasm_memory_t* wasm_memory;
+ wasm_engine_t *wasm_engine;
+ wasm_extern_vec_t wasm_imports;
+
+ int argc;
+ char **argv;
+
+ // HACK HACK HACK
+ // There should need to be this much stuff in here, but because Wasmer doesn't ship a "wasmerdll.lib"
+ // file for windows, it is impossible for it to link successfully against the function provided in onyx.exe.
+ // Therefore, we must serve as the linker and do this manually. Hopefully that library file will be
+ // shipped soon so this can go away...
+ char* (*wasm_memory_data)(wasm_memory_t *wasm_memory);
+ wasm_extern_t* (*wasm_extern_lookup_by_name)(wasm_module_t* module, wasm_instance_t* instance, const char* name);
+ wasm_func_t* (*wasm_extern_as_func)(wasm_extern_t* ext);
+ wasm_trap_t* (*wasm_func_call)(const wasm_func_t* wasm_func, const wasm_val_vec_t* args, wasm_val_vec_t* results);
+ wasm_instance_t* (*wasm_instance_new)(wasm_store_t* store, const wasm_module_t* module, const wasm_extern_vec_t* imports, wasm_trap_t** traps);
+ void (*onyx_print_trap)(wasm_trap_t* trap);
+ wasm_store_t *(*wasm_store_new)(wasm_engine_t *engine);
+ void (*wasm_store_delete)(wasm_store_t *store);
+} OnyxRuntime;
+
+OnyxRuntime* runtime;
+
+typedef struct WasmValkindBuffer {
+ unsigned int count;
+ wasm_valkind_t types[20];
+} WasmValkindBuffer;
+
+typedef struct WasmFuncDefinition {
+ char* module_name;
+ char* import_name;
+ wasm_func_callback_t func;
+
+ WasmValkindBuffer *params;
+ WasmValkindBuffer *results;
+} WasmFuncDefinition;
+
+#define STRINGIFY1(a) #a
+#define STRINGIFY2(a) STRINGIFY1(a)
+#define CONCAT2(a, b) a ## _ ## b
+#define CONCAT3(a, b, c) a ## _ ## b ## _ ## c
+#define ONYX_MODULE_NAME_GEN(m) CONCAT2(__onyx_library, m)
+#define ONYX_LINK_NAME_GEN(m) CONCAT2(onyx_library, m)
+#define ONYX_FUNC_NAME(m, n) CONCAT3(__onyx_internal, m, n)
+#define ONYX_DEF_NAME(m, n) CONCAT3(__onyx_internal_def, m, n)
+#define ONYX_PARAM_NAME(m, n) CONCAT3(__onyx_internal_param_buffer, m, n)
+#define ONYX_RESULT_NAME(m, n) CONCAT3(__onyx_internal_result_buffer, m, n)
+#define ONYX_IMPORT_NAME(m, n) STRINGIFY1(m) "_" #n
+
+#define NUM_VALS(...) (sizeof((wasm_valkind_t []){ 0, __VA_ARGS__ }) / sizeof(wasm_valkind_t))
+#define _VALS(...) { NUM_VALS(__VA_ARGS__) - 1, __VA_ARGS__ }
+
+#define ONYX_DEF(name, params_types, result_types) \
+ static wasm_trap_t* ONYX_FUNC_NAME(ONYX_LIBRARY_NAME, name)(const wasm_val_vec_t* params, wasm_val_vec_t* results); \
+ static struct WasmValkindBuffer ONYX_PARAM_NAME(ONYX_LIBRARY_NAME, name) = _VALS params_types; \
+ static struct WasmValkindBuffer ONYX_RESULT_NAME(ONYX_LIBRARY_NAME, name) = _VALS result_types; \
+ static struct WasmFuncDefinition ONYX_DEF_NAME(ONYX_LIBRARY_NAME, name) = { STRINGIFY2(ONYX_LIBRARY_NAME), #name, ONYX_FUNC_NAME(ONYX_LIBRARY_NAME, name), & ONYX_PARAM_NAME(ONYX_LIBRARY_NAME, name), & ONYX_RESULT_NAME(ONYX_LIBRARY_NAME, name) }; \
+ \
+ static wasm_trap_t* ONYX_FUNC_NAME(ONYX_LIBRARY_NAME, name)(const wasm_val_vec_t* params, wasm_val_vec_t* results)
+
+#define ONYX_FUNC(name) & ONYX_DEF_NAME(ONYX_LIBRARY_NAME, name),
+#define ONYX_LIBRARY \
+ extern struct WasmFuncDefinition *ONYX_MODULE_NAME_GEN(ONYX_LIBRARY_NAME)[]; \
+ ONYX_EXPORT WasmFuncDefinition** ONYX_LINK_NAME_GEN(ONYX_LIBRARY_NAME)(OnyxRuntime* in_runtime) { \
+ runtime = in_runtime; \
+ return ONYX_MODULE_NAME_GEN(ONYX_LIBRARY_NAME); \
+ } \
+ struct WasmFuncDefinition *ONYX_MODULE_NAME_GEN(ONYX_LIBRARY_NAME)[] =
+
+// Shorter names
+#ifndef ONYX_NO_SHORT_NAMES
+#undef BOOL
+#undef INT
+#undef LONG
+#undef FLOAT
+#undef DOUBLE
+#define BOOL WASM_I32
+#define INT WASM_I32
+#define LONG WASM_I64
+#define FLOAT WASM_F32
+#define DOUBLE WASM_F64
+#define PTR WASM_I32
+#endif
+
+#define ONYX_PTR(p) ((void*) (p != 0 ? (runtime->wasm_memory_data(runtime->wasm_memory) + p) : NULL))
--- /dev/null
+// Bill's Mini Windows.h from https://github.com/odin-lang/Odin/blob/master/src/gb/gb.h
+
+////////////////////////////////////////////////////////////////
+//
+// Bill's Mini Windows.h
+//
+//
+
+#define GB_EXTERN extern
+#define GB_DLL_EXPORT GB_EXTERN __declspec(dllexport)
+#define GB_DLL_IMPORT GB_EXTERN __declspec(dllimport)
+
+#define WINAPI __stdcall
+#define WINAPIV __cdecl
+#define CALLBACK __stdcall
+#define MAX_PATH 260
+#define CCHDEVICENAME 32
+#define CCHFORMNAME 32
+
+typedef unsigned long DWORD;
+typedef int WINBOOL;
+#ifndef XFree86Server
+ #ifndef __OBJC__
+ typedef WINBOOL BOOL;
+ #else
+ #define BOOL WINBOOL
+ #endif
+typedef unsigned char BYTE;
+#endif
+typedef unsigned short WORD;
+typedef float FLOAT;
+typedef int INT;
+typedef unsigned int UINT;
+typedef short SHORT;
+typedef long LONG;
+typedef long long LONGLONG;
+typedef unsigned short USHORT;
+typedef unsigned long ULONG;
+typedef unsigned long long ULONGLONG;
+
+typedef UINT WPARAM;
+typedef LONG LPARAM;
+typedef LONG LRESULT;
+#ifndef _HRESULT_DEFINED
+typedef LONG HRESULT;
+#define _HRESULT_DEFINED
+#endif
+#ifndef XFree86Server
+typedef WORD ATOM;
+#endif /* XFree86Server */
+typedef void *HANDLE;
+typedef HANDLE HGLOBAL;
+typedef HANDLE HLOCAL;
+typedef HANDLE GLOBALHANDLE;
+typedef HANDLE LOCALHANDLE;
+typedef void *HGDIOBJ;
+
+#define DECLARE_HANDLE(name) typedef HANDLE name
+DECLARE_HANDLE(HACCEL);
+DECLARE_HANDLE(HBITMAP);
+DECLARE_HANDLE(HBRUSH);
+DECLARE_HANDLE(HCOLORSPACE);
+DECLARE_HANDLE(HDC);
+DECLARE_HANDLE(HGLRC);
+DECLARE_HANDLE(HDESK);
+DECLARE_HANDLE(HENHMETAFILE);
+DECLARE_HANDLE(HFONT);
+DECLARE_HANDLE(HICON);
+DECLARE_HANDLE(HKEY);
+typedef HKEY *PHKEY;
+DECLARE_HANDLE(HMENU);
+DECLARE_HANDLE(HMETAFILE);
+DECLARE_HANDLE(HINSTANCE);
+typedef HINSTANCE HMODULE;
+DECLARE_HANDLE(HPALETTE);
+DECLARE_HANDLE(HPEN);
+DECLARE_HANDLE(HRGN);
+DECLARE_HANDLE(HRSRC);
+DECLARE_HANDLE(HSTR);
+DECLARE_HANDLE(HTASK);
+DECLARE_HANDLE(HWND);
+DECLARE_HANDLE(HWINSTA);
+DECLARE_HANDLE(HKL);
+DECLARE_HANDLE(HRAWINPUT);
+DECLARE_HANDLE(HMONITOR);
+#undef DECLARE_HANDLE
+
+typedef int HFILE;
+typedef HICON HCURSOR;
+typedef DWORD COLORREF;
+typedef int (WINAPI *FARPROC)();
+typedef int (WINAPI *NEARPROC)();
+typedef int (WINAPI *PROC)();
+typedef LRESULT (CALLBACK *WNDPROC)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+#if defined(_WIN64)
+typedef unsigned __int64 ULONG_PTR;
+typedef signed __int64 LONG_PTR;
+#else
+typedef unsigned long ULONG_PTR;
+typedef signed long LONG_PTR;
+#endif
+typedef ULONG_PTR DWORD_PTR;
+
+typedef struct tagRECT {
+ LONG left;
+ LONG top;
+ LONG right;
+ LONG bottom;
+} RECT;
+typedef struct tagRECTL {
+ LONG left;
+ LONG top;
+ LONG right;
+ LONG bottom;
+} RECTL;
+typedef struct tagPOINT {
+ LONG x;
+ LONG y;
+} POINT;
+typedef struct tagSIZE {
+ LONG cx;
+ LONG cy;
+} SIZE;
+typedef struct tagPOINTS {
+ SHORT x;
+ SHORT y;
+} POINTS;
+typedef struct _SECURITY_ATTRIBUTES {
+ DWORD nLength;
+ HANDLE lpSecurityDescriptor;
+ BOOL bInheritHandle;
+} SECURITY_ATTRIBUTES;
+typedef enum _LOGICAL_PROCESSOR_RELATIONSHIP {
+ RelationProcessorCore,
+ RelationNumaNode,
+ RelationCache,
+ RelationProcessorPackage,
+ RelationGroup,
+ RelationAll = 0xffff
+} LOGICAL_PROCESSOR_RELATIONSHIP;
+typedef enum _PROCESSOR_CACHE_TYPE {
+ CacheUnified,
+ CacheInstruction,
+ CacheData,
+ CacheTrace
+} PROCESSOR_CACHE_TYPE;
+typedef struct _CACHE_DESCRIPTOR {
+ BYTE Level;
+ BYTE Associativity;
+ WORD LineSize;
+ DWORD Size;
+ PROCESSOR_CACHE_TYPE Type;
+} CACHE_DESCRIPTOR;
+typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION {
+ ULONG_PTR ProcessorMask;
+ LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
+ union {
+ struct {
+ BYTE Flags;
+ } ProcessorCore;
+ struct {
+ DWORD NodeNumber;
+ } NumaNode;
+ CACHE_DESCRIPTOR Cache;
+ ULONGLONG Reserved[2];
+ };
+} SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
+typedef struct _MEMORY_BASIC_INFORMATION {
+ void *BaseAddress;
+ void *AllocationBase;
+ DWORD AllocationProtect;
+ size_t RegionSize;
+ DWORD State;
+ DWORD Protect;
+ DWORD Type;
+} MEMORY_BASIC_INFORMATION;
+typedef struct _SYSTEM_INFO {
+ union {
+ DWORD dwOemId;
+ struct {
+ WORD wProcessorArchitecture;
+ WORD wReserved;
+ };
+ };
+ DWORD dwPageSize;
+ void * lpMinimumApplicationAddress;
+ void * lpMaximumApplicationAddress;
+ DWORD_PTR dwActiveProcessorMask;
+ DWORD dwNumberOfProcessors;
+ DWORD dwProcessorType;
+ DWORD dwAllocationGranularity;
+ WORD wProcessorLevel;
+ WORD wProcessorRevision;
+} SYSTEM_INFO;
+typedef union _LARGE_INTEGER {
+ struct {
+ DWORD LowPart;
+ LONG HighPart;
+ };
+ struct {
+ DWORD LowPart;
+ LONG HighPart;
+ } u;
+ LONGLONG QuadPart;
+} LARGE_INTEGER;
+typedef union _ULARGE_INTEGER {
+ struct {
+ DWORD LowPart;
+ DWORD HighPart;
+ };
+ struct {
+ DWORD LowPart;
+ DWORD HighPart;
+ } u;
+ ULONGLONG QuadPart;
+} ULARGE_INTEGER;
+
+typedef struct _OVERLAPPED {
+ ULONG_PTR Internal;
+ ULONG_PTR InternalHigh;
+ union {
+ struct {
+ DWORD Offset;
+ DWORD OffsetHigh;
+ };
+ void *Pointer;
+ };
+ HANDLE hEvent;
+} OVERLAPPED;
+typedef struct _FILETIME {
+ DWORD dwLowDateTime;
+ DWORD dwHighDateTime;
+} FILETIME;
+typedef struct _WIN32_FIND_DATAW {
+ DWORD dwFileAttributes;
+ FILETIME ftCreationTime;
+ FILETIME ftLastAccessTime;
+ FILETIME ftLastWriteTime;
+ DWORD nFileSizeHigh;
+ DWORD nFileSizeLow;
+ DWORD dwReserved0;
+ DWORD dwReserved1;
+ wchar_t cFileName[MAX_PATH];
+ wchar_t cAlternateFileName[14];
+} WIN32_FIND_DATAW;
+typedef struct _WIN32_FIND_DATAA {
+ DWORD dwFileAttributes;
+ FILETIME ftCreationTime;
+ FILETIME ftLastAccessTime;
+ FILETIME ftLastWriteTime;
+ DWORD nFileSizeHigh;
+ DWORD nFileSizeLow;
+ DWORD dwReserved0;
+ DWORD dwReserved1;
+ char cFileName[MAX_PATH];
+ char cAlternateFileName[14];
+} WIN32_FIND_DATAA;
+typedef struct _WIN32_FILE_ATTRIBUTE_DATA {
+ DWORD dwFileAttributes;
+ FILETIME ftCreationTime;
+ FILETIME ftLastAccessTime;
+ FILETIME ftLastWriteTime;
+ DWORD nFileSizeHigh;
+ DWORD nFileSizeLow;
+} WIN32_FILE_ATTRIBUTE_DATA;
+typedef enum _GET_FILEEX_INFO_LEVELS {
+ GetFileExInfoStandard,
+ GetFileExMaxInfoLevel
+} GET_FILEEX_INFO_LEVELS;
+typedef struct _STARTUPINFOA {
+ DWORD cb;
+ char * lpReserved;
+ char * lpDesktop;
+ char * lpTitle;
+ DWORD dwX;
+ DWORD dwY;
+ DWORD dwXSize;
+ DWORD dwYSize;
+ DWORD dwXCountChars;
+ DWORD dwYCountChars;
+ DWORD dwFillAttribute;
+ DWORD dwFlags;
+ WORD wShowWindow;
+ WORD cbReserved2;
+ char * lpReserved2;
+ HANDLE hStdInput;
+ HANDLE hStdOutput;
+ HANDLE hStdError;
+} STARTUPINFOA, *LPSTARTUPINFOA;
+typedef struct _PROCESS_INFORMATION {
+ HANDLE hProcess;
+ HANDLE hThread;
+ DWORD dwProcessId;
+ DWORD dwThreadId;
+} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
+
+#define INFINITE 0xffffffffl
+#define INVALID_HANDLE_VALUE ((void *)(intptr_t)(-1))
+#define STARTF_USESTDHANDLES 0x00000100
+
+typedef DWORD WINAPI THREAD_START_ROUTINE(void *parameter);
+
+GB_DLL_IMPORT DWORD WINAPI GetLastError (void);
+GB_DLL_IMPORT BOOL WINAPI CloseHandle (HANDLE object);
+GB_DLL_IMPORT HANDLE WINAPI CreateSemaphoreA (SECURITY_ATTRIBUTES *semaphore_attributes, LONG initial_count,
+ LONG maximum_count, char const *name);
+GB_DLL_IMPORT BOOL WINAPI ReleaseSemaphore (HANDLE semaphore, LONG release_count, LONG *previous_count);
+GB_DLL_IMPORT DWORD WINAPI WaitForSingleObject(HANDLE handle, DWORD milliseconds);
+GB_DLL_IMPORT HANDLE WINAPI CreateThread (SECURITY_ATTRIBUTES *semaphore_attributes, size_t stack_size,
+ THREAD_START_ROUTINE *start_address, void *parameter,
+ DWORD creation_flags, DWORD *thread_id);
+GB_DLL_IMPORT DWORD WINAPI GetThreadId (HANDLE handle);
+GB_DLL_IMPORT void WINAPI RaiseException (DWORD, DWORD, DWORD, ULONG_PTR const *);
+GB_DLL_IMPORT BOOL WINAPI TerminateThread (HANDLE hThread, DWORD dwExitCode);
+GB_DLL_IMPORT BOOL WINAPI CreateProcessA (char const * lpApplicationName, char * lpCommandLine,
+ SECURITY_ATTRIBUTES* lpProcessAttrs, SECURITY_ATTRIBUTES* lpThreadAttributes,
+ BOOL bInheritHandles, DWORD dwCreationFlags, void* lpEnvironment,
+ char const * lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation);
+GB_DLL_IMPORT BOOL WINAPI GetExitCodeProcess (HANDLE hProcess, DWORD *lpExitCode);
+GB_DLL_IMPORT BOOL WINAPI CreatePipe (HANDLE *hReadPipe, HANDLE *hWritePipe, SECURITY_ATTRIBUTES* lpPipeAttributes,
+ DWORD nSize);
+GB_DLL_IMPORT BOOL WINAPI TerminateProcess (HANDLE hProcess, UINT uExitCode);
+GB_DLL_IMPORT BOOL WINAPI SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags);
+
+uintptr_t _beginthreadex( // NATIVE CODE
+ void *security,
+ unsigned stack_size,
+ unsigned ( __stdcall *start_address )( void * ),
+ void *arglist,
+ unsigned initflag,
+ unsigned *thrdaddr
+);
+
+
+GB_DLL_IMPORT BOOL WINAPI GetLogicalProcessorInformation(SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buffer, DWORD *return_length);
+GB_DLL_IMPORT DWORD_PTR WINAPI SetThreadAffinityMask(HANDLE thread, DWORD_PTR check_mask);
+GB_DLL_IMPORT HANDLE WINAPI GetCurrentThread(void);
+
+#define PAGE_NOACCESS 0x01
+#define PAGE_READONLY 0x02
+#define PAGE_READWRITE 0x04
+#define PAGE_WRITECOPY 0x08
+#define PAGE_EXECUTE 0x10
+#define PAGE_EXECUTE_READ 0x20
+#define PAGE_EXECUTE_READWRITE 0x40
+#define PAGE_EXECUTE_WRITECOPY 0x80
+#define PAGE_GUARD 0x100
+#define PAGE_NOCACHE 0x200
+#define PAGE_WRITECOMBINE 0x400
+
+#define MEM_COMMIT 0x1000
+#define MEM_RESERVE 0x2000
+#define MEM_DECOMMIT 0x4000
+#define MEM_RELEASE 0x8000
+#define MEM_FREE 0x10000
+#define MEM_PRIVATE 0x20000
+#define MEM_MAPPED 0x40000
+#define MEM_RESET 0x80000
+#define MEM_TOP_DOWN 0x100000
+#define MEM_LARGE_PAGES 0x20000000
+#define MEM_4MB_PAGES 0x80000000
+
+
+
+
+GB_DLL_IMPORT void * WINAPI VirtualAlloc (void *addr, size_t size, DWORD allocation_type, DWORD protect);
+GB_DLL_IMPORT size_t WINAPI VirtualQuery (void const *address, MEMORY_BASIC_INFORMATION *buffer, size_t length);
+GB_DLL_IMPORT BOOL WINAPI VirtualFree (void *address, size_t size, DWORD free_type);
+GB_DLL_IMPORT void WINAPI GetSystemInfo(SYSTEM_INFO *system_info);
+
+
+#define GENERIC_READ 0x80000000
+#define GENERIC_WRITE 0x40000000
+#define GENERIC_EXECUTE 0x20000000
+#define GENERIC_ALL 0x10000000
+#define FILE_SHARE_READ 0x00000001
+#define FILE_SHARE_WRITE 0x00000002
+#define FILE_SHARE_DELETE 0x00000004
+#define CREATE_NEW 1
+#define CREATE_ALWAYS 2
+#define OPEN_EXISTING 3
+#define OPEN_ALWAYS 4
+#define TRUNCATE_EXISTING 5
+#define FILE_ATTRIBUTE_READONLY 0x00000001
+#define FILE_ATTRIBUTE_NORMAL 0x00000080
+#define FILE_ATTRIBUTE_TEMPORARY 0x00000100
+#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
+#define ERROR_FILE_NOT_FOUND 2l
+#define ERROR_ACCESS_DENIED 5L
+#define ERROR_NO_MORE_FILES 18l
+#define ERROR_FILE_EXISTS 80l
+#define ERROR_ALREADY_EXISTS 183l
+#define STD_INPUT_HANDLE ((DWORD)-10)
+#define STD_OUTPUT_HANDLE ((DWORD)-11)
+#define STD_ERROR_HANDLE ((DWORD)-12)
+
+GB_DLL_IMPORT int MultiByteToWideChar(UINT code_page, DWORD flags, char const * multi_byte_str, int multi_byte_len, wchar_t const *wide_char_str, int wide_char_len);
+GB_DLL_IMPORT int WideCharToMultiByte(UINT code_page, DWORD flags, wchar_t const *wide_char_str, int wide_char_len, char const * multi_byte_str, int multi_byte_len);
+GB_DLL_IMPORT BOOL WINAPI SetFilePointerEx(HANDLE file, LARGE_INTEGER distance_to_move,
+ LARGE_INTEGER *new_file_pointer, DWORD move_method);
+GB_DLL_IMPORT BOOL WINAPI ReadFile (HANDLE file, void *buffer, DWORD bytes_to_read, DWORD *bytes_read, OVERLAPPED *overlapped);
+GB_DLL_IMPORT BOOL WINAPI WriteFile (HANDLE file, void const *buffer, DWORD bytes_to_write, DWORD *bytes_written, OVERLAPPED *overlapped);
+GB_DLL_IMPORT HANDLE WINAPI CreateFileW (wchar_t const *path, DWORD desired_access, DWORD share_mode,
+ SECURITY_ATTRIBUTES *, DWORD creation_disposition,
+ DWORD flags_and_attributes, HANDLE template_file);
+GB_DLL_IMPORT HANDLE WINAPI CreateFileA (char const *path, DWORD desired_access, DWORD share_mode,
+ SECURITY_ATTRIBUTES *, DWORD creation_disposition,
+ DWORD flags_and_attributes, HANDLE template_file);
+GB_DLL_IMPORT HANDLE WINAPI GetStdHandle (DWORD std_handle);
+GB_DLL_IMPORT BOOL WINAPI GetFileSizeEx (HANDLE file, LARGE_INTEGER *size);
+GB_DLL_IMPORT BOOL WINAPI SetEndOfFile (HANDLE file);
+GB_DLL_IMPORT HANDLE WINAPI FindFirstFileW (wchar_t const *path, WIN32_FIND_DATAW *data);
+GB_DLL_IMPORT HANDLE WINAPI FindFirstFileA (char const *path, WIN32_FIND_DATAA *data);
+GB_DLL_IMPORT BOOL WINAPI FindNextFileA (HANDLE find_find, WIN32_FIND_DATAA *data);
+GB_DLL_IMPORT BOOL WINAPI FindClose (HANDLE find_file);
+GB_DLL_IMPORT BOOL WINAPI GetFileAttributesExW(wchar_t const *path, GET_FILEEX_INFO_LEVELS info_level_id, WIN32_FILE_ATTRIBUTE_DATA *data);
+GB_DLL_IMPORT BOOL WINAPI CopyFileW(wchar_t const *old_f, wchar_t const *new_f, BOOL fail_if_exists);
+GB_DLL_IMPORT BOOL WINAPI MoveFileW(wchar_t const *old_f, wchar_t const *new_f);
+GB_DLL_IMPORT BOOL WINAPI DeleteFileA (char const *path);
+GB_DLL_IMPORT BOOL WINAPI CreateDirectoryA(char const *path);
+GB_DLL_IMPORT BOOL WINAPI RemoveDirectoryA(char const *path);
+GB_DLL_IMPORT BOOL WINAPI MoveFileA (char const *old_path, char const *new_path);
+
+GB_DLL_IMPORT DWORD WINAPI GetFullPathNameA(char const *lpFileName, DWORD nBufferLength, char *lpBuffer, char **lpFilePart);
+
+GB_DLL_IMPORT HMODULE WINAPI LoadLibraryA (char const *filename);
+GB_DLL_IMPORT BOOL WINAPI FreeLibrary (HMODULE module);
+GB_DLL_IMPORT FARPROC WINAPI GetProcAddress(HMODULE module, char const *name);
+
+GB_DLL_IMPORT BOOL WINAPI QueryPerformanceFrequency(LARGE_INTEGER *frequency);
+GB_DLL_IMPORT BOOL WINAPI QueryPerformanceCounter (LARGE_INTEGER *counter);
+GB_DLL_IMPORT void WINAPI GetSystemTimeAsFileTime (FILETIME *system_time_as_file_time);
+GB_DLL_IMPORT void WINAPI Sleep(DWORD milliseconds);
+GB_DLL_IMPORT void WINAPI ExitProcess(UINT exit_code);
+
+GB_DLL_IMPORT BOOL WINAPI SetEnvironmentVariableA(char const *name, char const *value);
+GB_DLL_IMPORT DWORD WINAPI GetEnvironmentVariableA(char const * lpName, char * lpBuffer, DWORD nSize);
+
+GB_DLL_IMPORT short WINAPI htons(short hostshort);
+GB_DLL_IMPORT int WINAPI htonl(int hostint);
+GB_DLL_IMPORT short WINAPI ntohs(short netshort);
+GB_DLL_IMPORT int WINAPI ntohl(int netint);
--- /dev/null
+/* stb_ds.h - v0.67 - public domain data structures - Sean Barrett 2019
+
+ This is a single-header-file library that provides easy-to-use
+ dynamic arrays and hash tables for C (also works in C++).
+
+ For a gentle introduction:
+ http://nothings.org/stb_ds
+
+ To use this library, do this in *one* C or C++ file:
+ #define STB_DS_IMPLEMENTATION
+ #include "stb_ds.h"
+
+TABLE OF CONTENTS
+
+ Table of Contents
+ Compile-time options
+ License
+ Documentation
+ Notes
+ Notes - Dynamic arrays
+ Notes - Hash maps
+ Credits
+
+COMPILE-TIME OPTIONS
+
+ #define STBDS_NO_SHORT_NAMES
+
+ This flag needs to be set globally.
+
+ By default stb_ds exposes shorter function names that are not qualified
+ with the "stbds_" prefix. If these names conflict with the names in your
+ code, define this flag.
+
+ #define STBDS_SIPHASH_2_4
+
+ This flag only needs to be set in the file containing #define STB_DS_IMPLEMENTATION.
+
+ By default stb_ds.h hashes using a weaker variant of SipHash and a custom hash for
+ 4- and 8-byte keys. On 64-bit platforms, you can define the above flag to force
+ stb_ds.h to use specification-compliant SipHash-2-4 for all keys. Doing so makes
+ hash table insertion about 20% slower on 4- and 8-byte keys, 5% slower on
+ 64-byte keys, and 10% slower on 256-byte keys on my test computer.
+
+ #define STBDS_REALLOC(context,ptr,size) better_realloc
+ #define STBDS_FREE(context,ptr) better_free
+
+ These defines only need to be set in the file containing #define STB_DS_IMPLEMENTATION.
+
+ By default stb_ds uses stdlib realloc() and free() for memory management. You can
+ substitute your own functions instead by defining these symbols. You must either
+ define both, or neither. Note that at the moment, 'context' will always be NULL.
+ @TODO add an array/hash initialization function that takes a memory context pointer.
+
+ #define STBDS_UNIT_TESTS
+
+ Defines a function stbds_unit_tests() that checks the functioning of the data structures.
+
+ Note that on older versions of gcc (e.g. 5.x.x) you may need to build with '-std=c++0x'
+ (or equivalentally '-std=c++11') when using anonymous structures as seen on the web
+ page or in STBDS_UNIT_TESTS.
+
+LICENSE
+
+ Placed in the public domain and also MIT licensed.
+ See end of file for detailed license information.
+
+DOCUMENTATION
+
+ Dynamic Arrays
+
+ Non-function interface:
+
+ Declare an empty dynamic array of type T
+ T* foo = NULL;
+
+ Access the i'th item of a dynamic array 'foo' of type T, T* foo:
+ foo[i]
+
+ Functions (actually macros)
+
+ arrfree:
+ void arrfree(T*);
+ Frees the array.
+
+ arrlen:
+ ptrdiff_t arrlen(T*);
+ Returns the number of elements in the array.
+
+ arrlenu:
+ size_t arrlenu(T*);
+ Returns the number of elements in the array as an unsigned type.
+
+ arrpop:
+ T arrpop(T* a)
+ Removes the final element of the array and returns it.
+
+ arrput:
+ T arrput(T* a, T b);
+ Appends the item b to the end of array a. Returns b.
+
+ arrins:
+ T arrins(T* a, int p, T b);
+ Inserts the item b into the middle of array a, into a[p],
+ moving the rest of the array over. Returns b.
+
+ arrinsn:
+ void arrinsn(T* a, int p, int n);
+ Inserts n uninitialized items into array a starting at a[p],
+ moving the rest of the array over.
+
+ arraddnptr:
+ T* arraddnptr(T* a, int n)
+ Appends n uninitialized items onto array at the end.
+ Returns a pointer to the first uninitialized item added.
+
+ arraddnindex:
+ size_t arraddnindex(T* a, int n)
+ Appends n uninitialized items onto array at the end.
+ Returns the index of the first uninitialized item added.
+
+ arrdel:
+ void arrdel(T* a, int p);
+ Deletes the element at a[p], moving the rest of the array over.
+
+ arrdeln:
+ void arrdeln(T* a, int p, int n);
+ Deletes n elements starting at a[p], moving the rest of the array over.
+
+ arrdelswap:
+ void arrdelswap(T* a, int p);
+ Deletes the element at a[p], replacing it with the element from
+ the end of the array. O(1) performance.
+
+ arrsetlen:
+ void arrsetlen(T* a, int n);
+ Changes the length of the array to n. Allocates uninitialized
+ slots at the end if necessary.
+
+ arrsetcap:
+ size_t arrsetcap(T* a, int n);
+ Sets the length of allocated storage to at least n. It will not
+ change the length of the array.
+
+ arrcap:
+ size_t arrcap(T* a);
+ Returns the number of total elements the array can contain without
+ needing to be reallocated.
+
+ Hash maps & String hash maps
+
+ Given T is a structure type: struct { TK key; TV value; }. Note that some
+ functions do not require TV value and can have other fields. For string
+ hash maps, TK must be 'char *'.
+
+ Special interface:
+
+ stbds_rand_seed:
+ void stbds_rand_seed(size_t seed);
+ For security against adversarially chosen data, you should seed the
+ library with a strong random number. Or at least seed it with time().
+
+ stbds_hash_string:
+ size_t stbds_hash_string(char *str, size_t seed);
+ Returns a hash value for a string.
+
+ stbds_hash_bytes:
+ size_t stbds_hash_bytes(void *p, size_t len, size_t seed);
+ These functions hash an arbitrary number of bytes. The function
+ uses a custom hash for 4- and 8-byte data, and a weakened version
+ of SipHash for everything else. On 64-bit platforms you can get
+ specification-compliant SipHash-2-4 on all data by defining
+ STBDS_SIPHASH_2_4, at a significant cost in speed.
+
+ Non-function interface:
+
+ Declare an empty hash map of type T
+ T* foo = NULL;
+
+ Access the i'th entry in a hash table T* foo:
+ foo[i]
+
+ Function interface (actually macros):
+
+ hmfree
+ shfree
+ void hmfree(T*);
+ void shfree(T*);
+ Frees the hashmap and sets the pointer to NULL.
+
+ hmlen
+ shlen
+ ptrdiff_t hmlen(T*)
+ ptrdiff_t shlen(T*)
+ Returns the number of elements in the hashmap.
+
+ hmlenu
+ shlenu
+ size_t hmlenu(T*)
+ size_t shlenu(T*)
+ Returns the number of elements in the hashmap.
+
+ hmgeti
+ shgeti
+ hmgeti_ts
+ ptrdiff_t hmgeti(T*, TK key)
+ ptrdiff_t shgeti(T*, char* key)
+ ptrdiff_t hmgeti_ts(T*, TK key, ptrdiff_t tempvar)
+ Returns the index in the hashmap which has the key 'key', or -1
+ if the key is not present.
+
+ hmget
+ hmget_ts
+ shget
+ TV hmget(T*, TK key)
+ TV shget(T*, char* key)
+ TV hmget_ts(T*, TK key, ptrdiff_t tempvar)
+ Returns the value corresponding to 'key' in the hashmap.
+ The structure must have a 'value' field
+
+ hmgets
+ shgets
+ T hmgets(T*, TK key)
+ T shgets(T*, char* key)
+ Returns the structure corresponding to 'key' in the hashmap.
+
+ hmgetp
+ shgetp
+ hmgetp_ts
+ hmgetp_null
+ shgetp_null
+ T* hmgetp(T*, TK key)
+ T* shgetp(T*, char* key)
+ T* hmgetp_ts(T*, TK key, ptrdiff_t tempvar)
+ T* hmgetp_null(T*, TK key)
+ T* shgetp_null(T*, char *key)
+ Returns a pointer to the structure corresponding to 'key' in
+ the hashmap. Functions ending in "_null" return NULL if the key
+ is not present in the hashmap; the others return a pointer to a
+ structure holding the default value (but not the searched-for key).
+
+ hmdefault
+ shdefault
+ TV hmdefault(T*, TV value)
+ TV shdefault(T*, TV value)
+ Sets the default value for the hashmap, the value which will be
+ returned by hmget/shget if the key is not present.
+
+ hmdefaults
+ shdefaults
+ TV hmdefaults(T*, T item)
+ TV shdefaults(T*, T item)
+ Sets the default struct for the hashmap, the contents which will be
+ returned by hmgets/shgets if the key is not present.
+
+ hmput
+ shput
+ TV hmput(T*, TK key, TV value)
+ TV shput(T*, char* key, TV value)
+ Inserts a <key,value> pair into the hashmap. If the key is already
+ present in the hashmap, updates its value.
+
+ hmputs
+ shputs
+ T hmputs(T*, T item)
+ T shputs(T*, T item)
+ Inserts a struct with T.key into the hashmap. If the struct is already
+ present in the hashmap, updates it.
+
+ hmdel
+ shdel
+ int hmdel(T*, TK key)
+ int shdel(T*, char* key)
+ If 'key' is in the hashmap, deletes its entry and returns 1.
+ Otherwise returns 0.
+
+ Function interface (actually macros) for strings only:
+
+ sh_new_strdup
+ void sh_new_strdup(T*);
+ Overwrites the existing pointer with a newly allocated
+ string hashmap which will automatically allocate and free
+ each string key using realloc/free
+
+ sh_new_arena
+ void sh_new_arena(T*);
+ Overwrites the existing pointer with a newly allocated
+ string hashmap which will automatically allocate each string
+ key to a string arena. Every string key ever used by this
+ hash table remains in the arena until the arena is freed.
+ Additionally, any key which is deleted and reinserted will
+ be allocated multiple times in the string arena.
+
+NOTES
+
+ * These data structures are realloc'd when they grow, and the macro
+ "functions" write to the provided pointer. This means: (a) the pointer
+ must be an lvalue, and (b) the pointer to the data structure is not
+ stable, and you must maintain it the same as you would a realloc'd
+ pointer. For example, if you pass a pointer to a dynamic array to a
+ function which updates it, the function must return back the new
+ pointer to the caller. This is the price of trying to do this in C.
+
+ * The following are the only functions that are thread-safe on a single data
+ structure, i.e. can be run in multiple threads simultaneously on the same
+ data structure
+ hmlen shlen
+ hmlenu shlenu
+ hmget_ts shget_ts
+ hmgeti_ts shgeti_ts
+ hmgets_ts shgets_ts
+
+ * You iterate over the contents of a dynamic array and a hashmap in exactly
+ the same way, using arrlen/hmlen/shlen:
+
+ for (i=0; i < arrlen(foo); ++i)
+ ... foo[i] ...
+
+ * All operations except arrins/arrdel are O(1) amortized, but individual
+ operations can be slow, so these data structures may not be suitable
+ for real time use. Dynamic arrays double in capacity as needed, so
+ elements are copied an average of once. Hash tables double/halve
+ their size as needed, with appropriate hysteresis to maintain O(1)
+ performance.
+
+NOTES - DYNAMIC ARRAY
+
+ * If you know how long a dynamic array is going to be in advance, you can avoid
+ extra memory allocations by using arrsetlen to allocate it to that length in
+ advance and use foo[n] while filling it out, or arrsetcap to allocate the memory
+ for that length and use arrput/arrpush as normal.
+
+ * Unlike some other versions of the dynamic array, this version should
+ be safe to use with strict-aliasing optimizations.
+
+NOTES - HASH MAP
+
+ * For compilers other than GCC and clang (e.g. Visual Studio), for hmput/hmget/hmdel
+ and variants, the key must be an lvalue (so the macro can take the address of it).
+ Extensions are used that eliminate this requirement if you're using C99 and later
+ in GCC or clang, or if you're using C++ in GCC. But note that this can make your
+ code less portable.
+
+ * To test for presence of a key in a hashmap, just do 'hmgeti(foo,key) >= 0'.
+
+ * The iteration order of your data in the hashmap is determined solely by the
+ order of insertions and deletions. In particular, if you never delete, new
+ keys are always added at the end of the array. This will be consistent
+ across all platforms and versions of the library. However, you should not
+ attempt to serialize the internal hash table, as the hash is not consistent
+ between different platforms, and may change with future versions of the library.
+
+ * Use sh_new_arena() for string hashmaps that you never delete from. Initialize
+ with NULL if you're managing the memory for your strings, or your strings are
+ never freed (at least until the hashmap is freed). Otherwise, use sh_new_strdup().
+ @TODO: make an arena variant that garbage collects the strings with a trivial
+ copy collector into a new arena whenever the table shrinks / rebuilds. Since
+ current arena recommendation is to only use arena if it never deletes, then
+ this can just replace current arena implementation.
+
+ * If adversarial input is a serious concern and you're on a 64-bit platform,
+ enable STBDS_SIPHASH_2_4 (see the 'Compile-time options' section), and pass
+ a strong random number to stbds_rand_seed.
+
+ * The default value for the hash table is stored in foo[-1], so if you
+ use code like 'hmget(T,k)->value = 5' you can accidentally overwrite
+ the value stored by hmdefault if 'k' is not present.
+
+CREDITS
+
+ Sean Barrett -- library, idea for dynamic array API/implementation
+ Per Vognsen -- idea for hash table API/implementation
+ Rafael Sachetto -- arrpop()
+ github:HeroicKatora -- arraddn() reworking
+
+ Bugfixes:
+ Andy Durdin
+ Shane Liesegang
+ Vinh Truong
+ Andreas Molzer
+ github:hashitaku
+ github:srdjanstipic
+ Macoy Madson
+ Andreas Vennstrom
+ Tobias Mansfield-Williams
+*/
+
+#ifdef STBDS_UNIT_TESTS
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#ifndef INCLUDE_STB_DS_H
+#define INCLUDE_STB_DS_H
+
+#include <stddef.h>
+#include <string.h>
+
+#ifndef STBDS_NO_SHORT_NAMES
+#define arrlen stbds_arrlen
+#define arrlenu stbds_arrlenu
+#define arrput stbds_arrput
+#define arrpush stbds_arrput
+#define arrpop stbds_arrpop
+#define arrfree stbds_arrfree
+#define arraddn stbds_arraddn // deprecated, use one of the following instead:
+#define arraddnptr stbds_arraddnptr
+#define arraddnindex stbds_arraddnindex
+#define arrsetlen stbds_arrsetlen
+#define arrlast stbds_arrlast
+#define arrins stbds_arrins
+#define arrinsn stbds_arrinsn
+#define arrdel stbds_arrdel
+#define arrdeln stbds_arrdeln
+#define arrdelswap stbds_arrdelswap
+#define arrcap stbds_arrcap
+#define arrsetcap stbds_arrsetcap
+
+#define hmput stbds_hmput
+#define hmputs stbds_hmputs
+#define hmget stbds_hmget
+#define hmget_ts stbds_hmget_ts
+#define hmgets stbds_hmgets
+#define hmgetp stbds_hmgetp
+#define hmgetp_ts stbds_hmgetp_ts
+#define hmgetp_null stbds_hmgetp_null
+#define hmgeti stbds_hmgeti
+#define hmgeti_ts stbds_hmgeti_ts
+#define hmdel stbds_hmdel
+#define hmlen stbds_hmlen
+#define hmlenu stbds_hmlenu
+#define hmfree stbds_hmfree
+#define hmdefault stbds_hmdefault
+#define hmdefaults stbds_hmdefaults
+
+#define shput stbds_shput
+#define shputi stbds_shputi
+#define shputs stbds_shputs
+#define shget stbds_shget
+#define shgeti stbds_shgeti
+#define shgets stbds_shgets
+#define shgetp stbds_shgetp
+#define shgetp_null stbds_shgetp_null
+#define shdel stbds_shdel
+#define shlen stbds_shlen
+#define shlenu stbds_shlenu
+#define shfree stbds_shfree
+#define shdefault stbds_shdefault
+#define shdefaults stbds_shdefaults
+#define sh_new_arena stbds_sh_new_arena
+#define sh_new_strdup stbds_sh_new_strdup
+
+#define stralloc stbds_stralloc
+#define strreset stbds_strreset
+#endif
+
+#if defined(STBDS_REALLOC) && !defined(STBDS_FREE) || !defined(STBDS_REALLOC) && defined(STBDS_FREE)
+#error "You must define both STBDS_REALLOC and STBDS_FREE, or neither."
+#endif
+#if !defined(STBDS_REALLOC) && !defined(STBDS_FREE)
+#include <stdlib.h>
+#define STBDS_REALLOC(c,p,s) realloc(p,s)
+#define STBDS_FREE(c,p) free(p)
+#endif
+
+#ifdef _MSC_VER
+#define STBDS_NOTUSED(v) (void)(v)
+#else
+#define STBDS_NOTUSED(v) (void)sizeof(v)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// for security against attackers, seed the library with a random number, at least time() but stronger is better
+extern void stbds_rand_seed(size_t seed);
+
+// these are the hash functions used internally if you want to test them or use them for other purposes
+extern size_t stbds_hash_bytes(void *p, size_t len, size_t seed);
+extern size_t stbds_hash_string(char *str, size_t seed);
+
+// this is a simple string arena allocator, initialize with e.g. 'stbds_string_arena my_arena={0}'.
+typedef struct stbds_string_arena stbds_string_arena;
+extern char * stbds_stralloc(stbds_string_arena *a, char *str);
+extern void stbds_strreset(stbds_string_arena *a);
+
+// have to #define STBDS_UNIT_TESTS to call this
+extern void stbds_unit_tests(void);
+
+///////////////
+//
+// Everything below here is implementation details
+//
+
+extern void * stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap);
+extern void stbds_arrfreef(void *a);
+extern void stbds_hmfree_func(void *p, size_t elemsize);
+extern void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode);
+extern void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode);
+extern void * stbds_hmput_default(void *a, size_t elemsize);
+extern void * stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode);
+extern void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode);
+extern void * stbds_shmode_func(size_t elemsize, int mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#define STBDS_HAS_TYPEOF
+#ifdef __cplusplus
+//#define STBDS_HAS_LITERAL_ARRAY // this is currently broken for clang
+#endif
+#endif
+
+#if !defined(__cplusplus)
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#define STBDS_HAS_LITERAL_ARRAY
+#endif
+#endif
+
+// this macro takes the address of the argument, but on gcc/clang can accept rvalues
+#if defined(STBDS_HAS_LITERAL_ARRAY) && defined(STBDS_HAS_TYPEOF)
+ #if __clang__
+ #define STBDS_ADDRESSOF(typevar, value) ((__typeof__(typevar)[1]){value}) // literal array decays to pointer to value
+ #else
+ #define STBDS_ADDRESSOF(typevar, value) ((typeof(typevar)[1]){value}) // literal array decays to pointer to value
+ #endif
+#else
+#define STBDS_ADDRESSOF(typevar, value) &(value)
+#endif
+
+#define STBDS_OFFSETOF(var,field) ((char *) &(var)->field - (char *) (var))
+
+#define stbds_header(t) ((stbds_array_header *) (t) - 1)
+#define stbds_temp(t) stbds_header(t)->temp
+#define stbds_temp_key(t) (*(char **) stbds_header(t)->hash_table)
+
+#define stbds_arrsetcap(a,n) (stbds_arrgrow(a,0,n))
+#define stbds_arrsetlen(a,n) ((stbds_arrcap(a) < (size_t) (n) ? stbds_arrsetcap((a),(size_t)(n)),0 : 0), (a) ? stbds_header(a)->length = (size_t) (n) : 0)
+#define stbds_arrcap(a) ((a) ? stbds_header(a)->capacity : 0)
+#define stbds_arrlen(a) ((a) ? (ptrdiff_t) stbds_header(a)->length : 0)
+#define stbds_arrlenu(a) ((a) ? stbds_header(a)->length : 0)
+#define stbds_arrput(a,v) (stbds_arrmaybegrow(a,1), (a)[stbds_header(a)->length++] = (v))
+#define stbds_arrpush stbds_arrput // synonym
+#define stbds_arrpop(a) (stbds_header(a)->length--, (a)[stbds_header(a)->length])
+#define stbds_arraddn(a,n) ((void)(stbds_arraddnindex(a, n))) // deprecated, use one of the following instead:
+#define stbds_arraddnptr(a,n) (stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), &(a)[stbds_header(a)->length-(n)]) : (a))
+#define stbds_arraddnindex(a,n)(stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), stbds_header(a)->length-(n)) : stbds_arrlen(a))
+#define stbds_arraddnoff stbds_arraddnindex
+#define stbds_arrlast(a) ((a)[stbds_header(a)->length-1])
+#define stbds_arrfree(a) ((void) ((a) ? STBDS_FREE(NULL,stbds_header(a)) : (void)0), (a)=NULL)
+#define stbds_arrdel(a,i) stbds_arrdeln(a,i,1)
+#define stbds_arrdeln(a,i,n) (memmove(&(a)[i], &(a)[(i)+(n)], sizeof *(a) * (stbds_header(a)->length-(n)-(i))), stbds_header(a)->length -= (n))
+#define stbds_arrdelswap(a,i) ((a)[i] = stbds_arrlast(a), stbds_header(a)->length -= 1)
+#define stbds_arrinsn(a,i,n) (stbds_arraddn((a),(n)), memmove(&(a)[(i)+(n)], &(a)[i], sizeof *(a) * (stbds_header(a)->length-(n)-(i))))
+#define stbds_arrins(a,i,v) (stbds_arrinsn((a),(i),1), (a)[i]=(v))
+
+#define stbds_arrmaybegrow(a,n) ((!(a) || stbds_header(a)->length + (n) > stbds_header(a)->capacity) \
+ ? (stbds_arrgrow(a,n,0),0) : 0)
+
+#define stbds_arrgrow(a,b,c) ((a) = stbds_arrgrowf_wrapper((a), sizeof *(a), (b), (c)))
+
+#define stbds_hmput(t, k, v) \
+ ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, 0), \
+ (t)[stbds_temp((t)-1)].key = (k), \
+ (t)[stbds_temp((t)-1)].value = (v))
+
+#define stbds_hmputs(t, s) \
+ ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), &(s).key, sizeof (s).key, STBDS_HM_BINARY), \
+ (t)[stbds_temp((t)-1)] = (s))
+
+#define stbds_hmgeti(t,k) \
+ ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_HM_BINARY), \
+ stbds_temp((t)-1))
+
+#define stbds_hmgeti_ts(t,k,temp) \
+ ((t) = stbds_hmget_key_ts_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, &(temp), STBDS_HM_BINARY), \
+ (temp))
+
+#define stbds_hmgetp(t, k) \
+ ((void) stbds_hmgeti(t,k), &(t)[stbds_temp((t)-1)])
+
+#define stbds_hmgetp_ts(t, k, temp) \
+ ((void) stbds_hmgeti_ts(t,k,temp), &(t)[temp])
+
+#define stbds_hmdel(t,k) \
+ (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_BINARY)),(t)?stbds_temp((t)-1):0)
+
+#define stbds_hmdefault(t, v) \
+ ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1].value = (v))
+
+#define stbds_hmdefaults(t, s) \
+ ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1] = (s))
+
+#define stbds_hmfree(p) \
+ ((void) ((p) != NULL ? stbds_hmfree_func((p)-1,sizeof*(p)),0 : 0),(p)=NULL)
+
+#define stbds_hmgets(t, k) (*stbds_hmgetp(t,k))
+#define stbds_hmget(t, k) (stbds_hmgetp(t,k)->value)
+#define stbds_hmget_ts(t, k, temp) (stbds_hmgetp_ts(t,k,temp)->value)
+#define stbds_hmlen(t) ((t) ? (ptrdiff_t) stbds_header((t)-1)->length-1 : 0)
+#define stbds_hmlenu(t) ((t) ? stbds_header((t)-1)->length-1 : 0)
+#define stbds_hmgetp_null(t,k) (stbds_hmgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)])
+
+#define stbds_shput(t, k, v) \
+ ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \
+ (t)[stbds_temp((t)-1)].value = (v))
+
+#define stbds_shputi(t, k, v) \
+ ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \
+ (t)[stbds_temp((t)-1)].value = (v), stbds_temp((t)-1))
+
+#define stbds_shputs(t, s) \
+ ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (s).key, sizeof (s).key, STBDS_HM_STRING), \
+ (t)[stbds_temp((t)-1)] = (s), \
+ (t)[stbds_temp((t)-1)].key = stbds_temp_key((t)-1)) // above line overwrites whole structure, so must rewrite key here if it was allocated internally
+
+#define stbds_pshput(t, p) \
+ ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (p)->key, sizeof (p)->key, STBDS_HM_PTR_TO_STRING), \
+ (t)[stbds_temp((t)-1)] = (p))
+
+#define stbds_shgeti(t,k) \
+ ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \
+ stbds_temp((t)-1))
+
+#define stbds_pshgeti(t,k) \
+ ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_HM_PTR_TO_STRING), \
+ stbds_temp((t)-1))
+
+#define stbds_shgetp(t, k) \
+ ((void) stbds_shgeti(t,k), &(t)[stbds_temp((t)-1)])
+
+#define stbds_pshget(t, k) \
+ ((void) stbds_pshgeti(t,k), (t)[stbds_temp((t)-1)])
+
+#define stbds_shdel(t,k) \
+ (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_STRING)),(t)?stbds_temp((t)-1):0)
+#define stbds_pshdel(t,k) \
+ (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_OFFSETOF(*(t),key), STBDS_HM_PTR_TO_STRING)),(t)?stbds_temp((t)-1):0)
+
+#define stbds_sh_new_arena(t) \
+ ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_ARENA))
+#define stbds_sh_new_strdup(t) \
+ ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_STRDUP))
+
+#define stbds_shdefault(t, v) stbds_hmdefault(t,v)
+#define stbds_shdefaults(t, s) stbds_hmdefaults(t,s)
+
+#define stbds_shfree stbds_hmfree
+#define stbds_shlenu stbds_hmlenu
+
+#define stbds_shgets(t, k) (*stbds_shgetp(t,k))
+#define stbds_shget(t, k) (stbds_shgetp(t,k)->value)
+#define stbds_shgetp_null(t,k) (stbds_shgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)])
+#define stbds_shlen stbds_hmlen
+
+typedef struct
+{
+ size_t length;
+ size_t capacity;
+ void * hash_table;
+ ptrdiff_t temp;
+} stbds_array_header;
+
+typedef struct stbds_string_block
+{
+ struct stbds_string_block *next;
+ char storage[8];
+} stbds_string_block;
+
+struct stbds_string_arena
+{
+ stbds_string_block *storage;
+ size_t remaining;
+ unsigned char block;
+ unsigned char mode; // this isn't used by the string arena itself
+};
+
+#define STBDS_HM_BINARY 0
+#define STBDS_HM_STRING 1
+
+enum
+{
+ STBDS_SH_NONE,
+ STBDS_SH_DEFAULT,
+ STBDS_SH_STRDUP,
+ STBDS_SH_ARENA
+};
+
+#ifdef __cplusplus
+// in C we use implicit assignment from these void*-returning functions to T*.
+// in C++ these templates make the same code work
+template<class T> static T * stbds_arrgrowf_wrapper(T *a, size_t elemsize, size_t addlen, size_t min_cap) {
+ return (T*)stbds_arrgrowf((void *)a, elemsize, addlen, min_cap);
+}
+template<class T> static T * stbds_hmget_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) {
+ return (T*)stbds_hmget_key((void*)a, elemsize, key, keysize, mode);
+}
+template<class T> static T * stbds_hmget_key_ts_wrapper(T *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode) {
+ return (T*)stbds_hmget_key_ts((void*)a, elemsize, key, keysize, temp, mode);
+}
+template<class T> static T * stbds_hmput_default_wrapper(T *a, size_t elemsize) {
+ return (T*)stbds_hmput_default((void *)a, elemsize);
+}
+template<class T> static T * stbds_hmput_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) {
+ return (T*)stbds_hmput_key((void*)a, elemsize, key, keysize, mode);
+}
+template<class T> static T * stbds_hmdel_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode){
+ return (T*)stbds_hmdel_key((void*)a, elemsize, key, keysize, keyoffset, mode);
+}
+template<class T> static T * stbds_shmode_func_wrapper(T *, size_t elemsize, int mode) {
+ return (T*)stbds_shmode_func(elemsize, mode);
+}
+#else
+#define stbds_arrgrowf_wrapper stbds_arrgrowf
+#define stbds_hmget_key_wrapper stbds_hmget_key
+#define stbds_hmget_key_ts_wrapper stbds_hmget_key_ts
+#define stbds_hmput_default_wrapper stbds_hmput_default
+#define stbds_hmput_key_wrapper stbds_hmput_key
+#define stbds_hmdel_key_wrapper stbds_hmdel_key
+#define stbds_shmode_func_wrapper(t,e,m) stbds_shmode_func(e,m)
+#endif
+
+#endif // INCLUDE_STB_DS_H
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// IMPLEMENTATION
+//
+
+#ifdef STB_DS_IMPLEMENTATION
+#include <assert.h>
+#include <string.h>
+
+#ifndef STBDS_ASSERT
+#define STBDS_ASSERT_WAS_UNDEFINED
+#define STBDS_ASSERT(x) ((void) 0)
+#endif
+
+#ifdef STBDS_STATISTICS
+#define STBDS_STATS(x) x
+size_t stbds_array_grow;
+size_t stbds_hash_grow;
+size_t stbds_hash_shrink;
+size_t stbds_hash_rebuild;
+size_t stbds_hash_probes;
+size_t stbds_hash_alloc;
+size_t stbds_rehash_probes;
+size_t stbds_rehash_items;
+#else
+#define STBDS_STATS(x)
+#endif
+
+//
+// stbds_arr implementation
+//
+
+//int *prev_allocs[65536];
+//int num_prev;
+
+void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap)
+{
+ stbds_array_header temp={0}; // force debugging
+ void *b;
+ size_t min_len = stbds_arrlen(a) + addlen;
+ (void) sizeof(temp);
+
+ // compute the minimum capacity needed
+ if (min_len > min_cap)
+ min_cap = min_len;
+
+ if (min_cap <= stbds_arrcap(a))
+ return a;
+
+ // increase needed capacity to guarantee O(1) amortized
+ if (min_cap < 2 * stbds_arrcap(a))
+ min_cap = 2 * stbds_arrcap(a);
+ else if (min_cap < 4)
+ min_cap = 4;
+
+ //if (num_prev < 65536) if (a) prev_allocs[num_prev++] = (int *) ((char *) a+1);
+ //if (num_prev == 2201)
+ // num_prev = num_prev;
+ b = STBDS_REALLOC(NULL, (a) ? stbds_header(a) : 0, elemsize * min_cap + sizeof(stbds_array_header));
+ //if (num_prev < 65536) prev_allocs[num_prev++] = (int *) (char *) b;
+ b = (char *) b + sizeof(stbds_array_header);
+ if (a == NULL) {
+ stbds_header(b)->length = 0;
+ stbds_header(b)->hash_table = 0;
+ stbds_header(b)->temp = 0;
+ } else {
+ STBDS_STATS(++stbds_array_grow);
+ }
+ stbds_header(b)->capacity = min_cap;
+
+ return b;
+}
+
+void stbds_arrfreef(void *a)
+{
+ STBDS_FREE(NULL, stbds_header(a));
+}
+
+//
+// stbds_hm hash table implementation
+//
+
+#ifdef STBDS_INTERNAL_SMALL_BUCKET
+#define STBDS_BUCKET_LENGTH 4
+#else
+#define STBDS_BUCKET_LENGTH 8
+#endif
+
+#define STBDS_BUCKET_SHIFT (STBDS_BUCKET_LENGTH == 8 ? 3 : 2)
+#define STBDS_BUCKET_MASK (STBDS_BUCKET_LENGTH-1)
+#define STBDS_CACHE_LINE_SIZE 64
+
+#define STBDS_ALIGN_FWD(n,a) (((n) + (a) - 1) & ~((a)-1))
+
+typedef struct
+{
+ size_t hash [STBDS_BUCKET_LENGTH];
+ ptrdiff_t index[STBDS_BUCKET_LENGTH];
+} stbds_hash_bucket; // in 32-bit, this is one 64-byte cache line; in 64-bit, each array is one 64-byte cache line
+
+typedef struct
+{
+ char * temp_key; // this MUST be the first field of the hash table
+ size_t slot_count;
+ size_t used_count;
+ size_t used_count_threshold;
+ size_t used_count_shrink_threshold;
+ size_t tombstone_count;
+ size_t tombstone_count_threshold;
+ size_t seed;
+ size_t slot_count_log2;
+ stbds_string_arena string;
+ stbds_hash_bucket *storage; // not a separate allocation, just 64-byte aligned storage after this struct
+} stbds_hash_index;
+
+#define STBDS_INDEX_EMPTY -1
+#define STBDS_INDEX_DELETED -2
+#define STBDS_INDEX_IN_USE(x) ((x) >= 0)
+
+#define STBDS_HASH_EMPTY 0
+#define STBDS_HASH_DELETED 1
+
+static size_t stbds_hash_seed=0x31415926;
+
+void stbds_rand_seed(size_t seed)
+{
+ stbds_hash_seed = seed;
+}
+
+#define stbds_load_32_or_64(var, temp, v32, v64_hi, v64_lo) \
+ temp = v64_lo ^ v32, temp <<= 16, temp <<= 16, temp >>= 16, temp >>= 16, /* discard if 32-bit */ \
+ var = v64_hi, var <<= 16, var <<= 16, /* discard if 32-bit */ \
+ var ^= temp ^ v32
+
+#define STBDS_SIZE_T_BITS ((sizeof (size_t)) * 8)
+
+static size_t stbds_probe_position(size_t hash, size_t slot_count, size_t slot_log2)
+{
+ size_t pos;
+ STBDS_NOTUSED(slot_log2);
+ pos = hash & (slot_count-1);
+ #ifdef STBDS_INTERNAL_BUCKET_START
+ pos &= ~STBDS_BUCKET_MASK;
+ #endif
+ return pos;
+}
+
+static size_t stbds_log2(size_t slot_count)
+{
+ size_t n=0;
+ while (slot_count > 1) {
+ slot_count >>= 1;
+ ++n;
+ }
+ return n;
+}
+
+static stbds_hash_index *stbds_make_hash_index(size_t slot_count, stbds_hash_index *ot)
+{
+ stbds_hash_index *t;
+ t = (stbds_hash_index *) STBDS_REALLOC(NULL,0,(slot_count >> STBDS_BUCKET_SHIFT) * sizeof(stbds_hash_bucket) + sizeof(stbds_hash_index) + STBDS_CACHE_LINE_SIZE-1);
+ t->storage = (stbds_hash_bucket *) STBDS_ALIGN_FWD((size_t) (t+1), STBDS_CACHE_LINE_SIZE);
+ t->slot_count = slot_count;
+ t->slot_count_log2 = stbds_log2(slot_count);
+ t->tombstone_count = 0;
+ t->used_count = 0;
+
+ #if 0 // A1
+ t->used_count_threshold = slot_count*12/16; // if 12/16th of table is occupied, grow
+ t->tombstone_count_threshold = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
+ t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink
+ #elif 1 // A2
+ //t->used_count_threshold = slot_count*12/16; // if 12/16th of table is occupied, grow
+ //t->tombstone_count_threshold = slot_count* 3/16; // if tombstones are 3/16th of table, rebuild
+ //t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink
+
+ // compute without overflowing
+ t->used_count_threshold = slot_count - (slot_count>>2);
+ t->tombstone_count_threshold = (slot_count>>3) + (slot_count>>4);
+ t->used_count_shrink_threshold = slot_count >> 2;
+
+ #elif 0 // B1
+ t->used_count_threshold = slot_count*13/16; // if 13/16th of table is occupied, grow
+ t->tombstone_count_threshold = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
+ t->used_count_shrink_threshold = slot_count* 5/16; // if table is only 5/16th full, shrink
+ #else // C1
+ t->used_count_threshold = slot_count*14/16; // if 14/16th of table is occupied, grow
+ t->tombstone_count_threshold = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
+ t->used_count_shrink_threshold = slot_count* 6/16; // if table is only 6/16th full, shrink
+ #endif
+ // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2
+ // Note that the larger tables have high variance as they were run fewer times
+ // A1 A2 B1 C1
+ // 0.10ms : 0.10ms : 0.10ms : 0.11ms : 2,000 inserts creating 2K table
+ // 0.96ms : 0.95ms : 0.97ms : 1.04ms : 20,000 inserts creating 20K table
+ // 14.48ms : 14.46ms : 10.63ms : 11.00ms : 200,000 inserts creating 200K table
+ // 195.74ms : 196.35ms : 203.69ms : 214.92ms : 2,000,000 inserts creating 2M table
+ // 2193.88ms : 2209.22ms : 2285.54ms : 2437.17ms : 20,000,000 inserts creating 20M table
+ // 65.27ms : 53.77ms : 65.33ms : 65.47ms : 500,000 inserts & deletes in 2K table
+ // 72.78ms : 62.45ms : 71.95ms : 72.85ms : 500,000 inserts & deletes in 20K table
+ // 89.47ms : 77.72ms : 96.49ms : 96.75ms : 500,000 inserts & deletes in 200K table
+ // 97.58ms : 98.14ms : 97.18ms : 97.53ms : 500,000 inserts & deletes in 2M table
+ // 118.61ms : 119.62ms : 120.16ms : 118.86ms : 500,000 inserts & deletes in 20M table
+ // 192.11ms : 194.39ms : 196.38ms : 195.73ms : 500,000 inserts & deletes in 200M table
+
+ if (slot_count <= STBDS_BUCKET_LENGTH)
+ t->used_count_shrink_threshold = 0;
+ // to avoid infinite loop, we need to guarantee that at least one slot is empty and will terminate probes
+ STBDS_ASSERT(t->used_count_threshold + t->tombstone_count_threshold < t->slot_count);
+ STBDS_STATS(++stbds_hash_alloc);
+ if (ot) {
+ t->string = ot->string;
+ // reuse old seed so we can reuse old hashes so below "copy out old data" doesn't do any hashing
+ t->seed = ot->seed;
+ } else {
+ size_t a,b,temp;
+ memset(&t->string, 0, sizeof(t->string));
+ t->seed = stbds_hash_seed;
+ // LCG
+ // in 32-bit, a = 2147001325 b = 715136305
+ // in 64-bit, a = 2862933555777941757 b = 3037000493
+ stbds_load_32_or_64(a,temp, 2147001325, 0x27bb2ee6, 0x87b0b0fd);
+ stbds_load_32_or_64(b,temp, 715136305, 0, 0xb504f32d);
+ stbds_hash_seed = stbds_hash_seed * a + b;
+ }
+
+ {
+ size_t i,j;
+ for (i=0; i < slot_count >> STBDS_BUCKET_SHIFT; ++i) {
+ stbds_hash_bucket *b = &t->storage[i];
+ for (j=0; j < STBDS_BUCKET_LENGTH; ++j)
+ b->hash[j] = STBDS_HASH_EMPTY;
+ for (j=0; j < STBDS_BUCKET_LENGTH; ++j)
+ b->index[j] = STBDS_INDEX_EMPTY;
+ }
+ }
+
+ // copy out the old data, if any
+ if (ot) {
+ size_t i,j;
+ t->used_count = ot->used_count;
+ for (i=0; i < ot->slot_count >> STBDS_BUCKET_SHIFT; ++i) {
+ stbds_hash_bucket *ob = &ot->storage[i];
+ for (j=0; j < STBDS_BUCKET_LENGTH; ++j) {
+ if (STBDS_INDEX_IN_USE(ob->index[j])) {
+ size_t hash = ob->hash[j];
+ size_t pos = stbds_probe_position(hash, t->slot_count, t->slot_count_log2);
+ size_t step = STBDS_BUCKET_LENGTH;
+ STBDS_STATS(++stbds_rehash_items);
+ for (;;) {
+ size_t limit,z;
+ stbds_hash_bucket *bucket;
+ bucket = &t->storage[pos >> STBDS_BUCKET_SHIFT];
+ STBDS_STATS(++stbds_rehash_probes);
+
+ for (z=pos & STBDS_BUCKET_MASK; z < STBDS_BUCKET_LENGTH; ++z) {
+ if (bucket->hash[z] == 0) {
+ bucket->hash[z] = hash;
+ bucket->index[z] = ob->index[j];
+ goto done;
+ }
+ }
+
+ limit = pos & STBDS_BUCKET_MASK;
+ for (z = 0; z < limit; ++z) {
+ if (bucket->hash[z] == 0) {
+ bucket->hash[z] = hash;
+ bucket->index[z] = ob->index[j];
+ goto done;
+ }
+ }
+
+ pos += step; // quadratic probing
+ step += STBDS_BUCKET_LENGTH;
+ pos &= (t->slot_count-1);
+ }
+ }
+ done:
+ ;
+ }
+ }
+ }
+
+ return t;
+}
+
+#define STBDS_ROTATE_LEFT(val, n) (((val) << (n)) | ((val) >> (STBDS_SIZE_T_BITS - (n))))
+#define STBDS_ROTATE_RIGHT(val, n) (((val) >> (n)) | ((val) << (STBDS_SIZE_T_BITS - (n))))
+
+size_t stbds_hash_string(char *str, size_t seed)
+{
+ size_t hash = seed;
+ while (*str)
+ hash = STBDS_ROTATE_LEFT(hash, 9) + (unsigned char) *str++;
+
+ // Thomas Wang 64-to-32 bit mix function, hopefully also works in 32 bits
+ hash ^= seed;
+ hash = (~hash) + (hash << 18);
+ hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,31);
+ hash = hash * 21;
+ hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,11);
+ hash += (hash << 6);
+ hash ^= STBDS_ROTATE_RIGHT(hash,22);
+ return hash+seed;
+}
+
+#ifdef STBDS_SIPHASH_2_4
+#define STBDS_SIPHASH_C_ROUNDS 2
+#define STBDS_SIPHASH_D_ROUNDS 4
+typedef int STBDS_SIPHASH_2_4_can_only_be_used_in_64_bit_builds[sizeof(size_t) == 8 ? 1 : -1];
+#endif
+
+#ifndef STBDS_SIPHASH_C_ROUNDS
+#define STBDS_SIPHASH_C_ROUNDS 1
+#endif
+#ifndef STBDS_SIPHASH_D_ROUNDS
+#define STBDS_SIPHASH_D_ROUNDS 1
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4127) // conditional expression is constant, for do..while(0) and sizeof()==
+#endif
+
+static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed)
+{
+ unsigned char *d = (unsigned char *) p;
+ size_t i,j;
+ size_t v0,v1,v2,v3, data;
+
+ // hash that works on 32- or 64-bit registers without knowing which we have
+ // (computes different results on 32-bit and 64-bit platform)
+ // derived from siphash, but on 32-bit platforms very different as it uses 4 32-bit state not 4 64-bit
+ v0 = ((((size_t) 0x736f6d65 << 16) << 16) + 0x70736575) ^ seed;
+ v1 = ((((size_t) 0x646f7261 << 16) << 16) + 0x6e646f6d) ^ ~seed;
+ v2 = ((((size_t) 0x6c796765 << 16) << 16) + 0x6e657261) ^ seed;
+ v3 = ((((size_t) 0x74656462 << 16) << 16) + 0x79746573) ^ ~seed;
+
+ #ifdef STBDS_TEST_SIPHASH_2_4
+ // hardcoded with key material in the siphash test vectors
+ v0 ^= 0x0706050403020100ull ^ seed;
+ v1 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed;
+ v2 ^= 0x0706050403020100ull ^ seed;
+ v3 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed;
+ #endif
+
+ #define STBDS_SIPROUND() \
+ do { \
+ v0 += v1; v1 = STBDS_ROTATE_LEFT(v1, 13); v1 ^= v0; v0 = STBDS_ROTATE_LEFT(v0,STBDS_SIZE_T_BITS/2); \
+ v2 += v3; v3 = STBDS_ROTATE_LEFT(v3, 16); v3 ^= v2; \
+ v2 += v1; v1 = STBDS_ROTATE_LEFT(v1, 17); v1 ^= v2; v2 = STBDS_ROTATE_LEFT(v2,STBDS_SIZE_T_BITS/2); \
+ v0 += v3; v3 = STBDS_ROTATE_LEFT(v3, 21); v3 ^= v0; \
+ } while (0)
+
+ for (i=0; i+sizeof(size_t) <= len; i += sizeof(size_t), d += sizeof(size_t)) {
+ data = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
+ data |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // discarded if size_t == 4
+
+ v3 ^= data;
+ for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)
+ STBDS_SIPROUND();
+ v0 ^= data;
+ }
+ data = len << (STBDS_SIZE_T_BITS-8);
+ switch (len - i) {
+ case 7: data |= ((size_t) d[6] << 24) << 24; // fall through
+ case 6: data |= ((size_t) d[5] << 20) << 20; // fall through
+ case 5: data |= ((size_t) d[4] << 16) << 16; // fall through
+ case 4: data |= (d[3] << 24); // fall through
+ case 3: data |= (d[2] << 16); // fall through
+ case 2: data |= (d[1] << 8); // fall through
+ case 1: data |= d[0]; // fall through
+ case 0: break;
+ }
+ v3 ^= data;
+ for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)
+ STBDS_SIPROUND();
+ v0 ^= data;
+ v2 ^= 0xff;
+ for (j=0; j < STBDS_SIPHASH_D_ROUNDS; ++j)
+ STBDS_SIPROUND();
+
+#ifdef STBDS_SIPHASH_2_4
+ return v0^v1^v2^v3;
+#else
+ return v1^v2^v3; // slightly stronger since v0^v3 in above cancels out final round operation? I tweeted at the authors of SipHash about this but they didn't reply
+#endif
+}
+
+size_t stbds_hash_bytes(void *p, size_t len, size_t seed)
+{
+#ifdef STBDS_SIPHASH_2_4
+ return stbds_siphash_bytes(p,len,seed);
+#else
+ unsigned char *d = (unsigned char *) p;
+
+ if (len == 4) {
+ unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
+ #if 0
+ // HASH32-A Bob Jenkin's hash function w/o large constants
+ hash ^= seed;
+ hash -= (hash<<6);
+ hash ^= (hash>>17);
+ hash -= (hash<<9);
+ hash ^= seed;
+ hash ^= (hash<<4);
+ hash -= (hash<<3);
+ hash ^= (hash<<10);
+ hash ^= (hash>>15);
+ #elif 1
+ // HASH32-BB Bob Jenkin's presumably-accidental version of Thomas Wang hash with rotates turned into shifts.
+ // Note that converting these back to rotates makes it run a lot slower, presumably due to collisions, so I'm
+ // not really sure what's going on.
+ hash ^= seed;
+ hash = (hash ^ 61) ^ (hash >> 16);
+ hash = hash + (hash << 3);
+ hash = hash ^ (hash >> 4);
+ hash = hash * 0x27d4eb2d;
+ hash ^= seed;
+ hash = hash ^ (hash >> 15);
+ #else // HASH32-C - Murmur3
+ hash ^= seed;
+ hash *= 0xcc9e2d51;
+ hash = (hash << 17) | (hash >> 15);
+ hash *= 0x1b873593;
+ hash ^= seed;
+ hash = (hash << 19) | (hash >> 13);
+ hash = hash*5 + 0xe6546b64;
+ hash ^= hash >> 16;
+ hash *= 0x85ebca6b;
+ hash ^= seed;
+ hash ^= hash >> 13;
+ hash *= 0xc2b2ae35;
+ hash ^= hash >> 16;
+ #endif
+ // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2
+ // Note that the larger tables have high variance as they were run fewer times
+ // HASH32-A // HASH32-BB // HASH32-C
+ // 0.10ms // 0.10ms // 0.10ms : 2,000 inserts creating 2K table
+ // 0.96ms // 0.95ms // 0.99ms : 20,000 inserts creating 20K table
+ // 14.69ms // 14.43ms // 14.97ms : 200,000 inserts creating 200K table
+ // 199.99ms // 195.36ms // 202.05ms : 2,000,000 inserts creating 2M table
+ // 2234.84ms // 2187.74ms // 2240.38ms : 20,000,000 inserts creating 20M table
+ // 55.68ms // 53.72ms // 57.31ms : 500,000 inserts & deletes in 2K table
+ // 63.43ms // 61.99ms // 65.73ms : 500,000 inserts & deletes in 20K table
+ // 80.04ms // 77.96ms // 81.83ms : 500,000 inserts & deletes in 200K table
+ // 100.42ms // 97.40ms // 102.39ms : 500,000 inserts & deletes in 2M table
+ // 119.71ms // 120.59ms // 121.63ms : 500,000 inserts & deletes in 20M table
+ // 185.28ms // 195.15ms // 187.74ms : 500,000 inserts & deletes in 200M table
+ // 15.58ms // 14.79ms // 15.52ms : 200,000 inserts creating 200K table with varying key spacing
+
+ return (((size_t) hash << 16 << 16) | hash) ^ seed;
+ } else if (len == 8 && sizeof(size_t) == 8) {
+ size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
+ hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4
+ hash ^= seed;
+ hash = (~hash) + (hash << 21);
+ hash ^= STBDS_ROTATE_RIGHT(hash,24);
+ hash *= 265;
+ hash ^= STBDS_ROTATE_RIGHT(hash,14);
+ hash ^= seed;
+ hash *= 21;
+ hash ^= STBDS_ROTATE_RIGHT(hash,28);
+ hash += (hash << 31);
+ hash = (~hash) + (hash << 18);
+ return hash;
+ } else {
+ return stbds_siphash_bytes(p,len,seed);
+ }
+#endif
+}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+
+static int stbds_is_key_equal(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode, size_t i)
+{
+ if (mode >= STBDS_HM_STRING)
+ return 0==strcmp((char *) key, * (char **) ((char *) a + elemsize*i + keyoffset));
+ else
+ return 0==memcmp(key, (char *) a + elemsize*i + keyoffset, keysize);
+}
+
+#define STBDS_HASH_TO_ARR(x,elemsize) ((char*) (x) - (elemsize))
+#define STBDS_ARR_TO_HASH(x,elemsize) ((char*) (x) + (elemsize))
+
+#define stbds_hash_table(a) ((stbds_hash_index *) stbds_header(a)->hash_table)
+
+void stbds_hmfree_func(void *a, size_t elemsize)
+{
+ if (a == NULL) return;
+ if (stbds_hash_table(a) != NULL) {
+ if (stbds_hash_table(a)->string.mode == STBDS_SH_STRDUP) {
+ size_t i;
+ // skip 0th element, which is default
+ for (i=1; i < stbds_header(a)->length; ++i)
+ STBDS_FREE(NULL, *(char**) ((char *) a + elemsize*i));
+ }
+ stbds_strreset(&stbds_hash_table(a)->string);
+ }
+ STBDS_FREE(NULL, stbds_header(a)->hash_table);
+ STBDS_FREE(NULL, stbds_header(a));
+}
+
+static ptrdiff_t stbds_hm_find_slot(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode)
+{
+ void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
+ stbds_hash_index *table = stbds_hash_table(raw_a);
+ size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed);
+ size_t step = STBDS_BUCKET_LENGTH;
+ size_t limit,i;
+ size_t pos;
+ stbds_hash_bucket *bucket;
+
+ if (hash < 2) hash += 2; // stored hash values are forbidden from being 0, so we can detect empty slots
+
+ pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2);
+
+ for (;;) {
+ STBDS_STATS(++stbds_hash_probes);
+ bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
+
+ // start searching from pos to end of bucket, this should help performance on small hash tables that fit in cache
+ for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) {
+ if (bucket->hash[i] == hash) {
+ if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
+ return (pos & ~STBDS_BUCKET_MASK)+i;
+ }
+ } else if (bucket->hash[i] == STBDS_HASH_EMPTY) {
+ return -1;
+ }
+ }
+
+ // search from beginning of bucket to pos
+ limit = pos & STBDS_BUCKET_MASK;
+ for (i = 0; i < limit; ++i) {
+ if (bucket->hash[i] == hash) {
+ if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
+ return (pos & ~STBDS_BUCKET_MASK)+i;
+ }
+ } else if (bucket->hash[i] == STBDS_HASH_EMPTY) {
+ return -1;
+ }
+ }
+
+ // quadratic probing
+ pos += step;
+ step += STBDS_BUCKET_LENGTH;
+ pos &= (table->slot_count-1);
+ }
+ /* NOTREACHED */
+}
+
+void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode)
+{
+ size_t keyoffset = 0;
+ if (a == NULL) {
+ // make it non-empty so we can return a temp
+ a = stbds_arrgrowf(0, elemsize, 0, 1);
+ stbds_header(a)->length += 1;
+ memset(a, 0, elemsize);
+ *temp = STBDS_INDEX_EMPTY;
+ // adjust a to point after the default element
+ return STBDS_ARR_TO_HASH(a,elemsize);
+ } else {
+ stbds_hash_index *table;
+ void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
+ // adjust a to point to the default element
+ table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;
+ if (table == 0) {
+ *temp = -1;
+ } else {
+ ptrdiff_t slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode);
+ if (slot < 0) {
+ *temp = STBDS_INDEX_EMPTY;
+ } else {
+ stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
+ *temp = b->index[slot & STBDS_BUCKET_MASK];
+ }
+ }
+ return a;
+ }
+}
+
+void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode)
+{
+ ptrdiff_t temp;
+ void *p = stbds_hmget_key_ts(a, elemsize, key, keysize, &temp, mode);
+ stbds_temp(STBDS_HASH_TO_ARR(p,elemsize)) = temp;
+ return p;
+}
+
+void * stbds_hmput_default(void *a, size_t elemsize)
+{
+ // three cases:
+ // a is NULL <- allocate
+ // a has a hash table but no entries, because of shmode <- grow
+ // a has entries <- do nothing
+ if (a == NULL || stbds_header(STBDS_HASH_TO_ARR(a,elemsize))->length == 0) {
+ a = stbds_arrgrowf(a ? STBDS_HASH_TO_ARR(a,elemsize) : NULL, elemsize, 0, 1);
+ stbds_header(a)->length += 1;
+ memset(a, 0, elemsize);
+ a=STBDS_ARR_TO_HASH(a,elemsize);
+ }
+ return a;
+}
+
+static char *stbds_strdup(char *str);
+
+void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode)
+{
+ size_t keyoffset=0;
+ void *raw_a;
+ stbds_hash_index *table;
+
+ if (a == NULL) {
+ a = stbds_arrgrowf(0, elemsize, 0, 1);
+ memset(a, 0, elemsize);
+ stbds_header(a)->length += 1;
+ // adjust a to point AFTER the default element
+ a = STBDS_ARR_TO_HASH(a,elemsize);
+ }
+
+ // adjust a to point to the default element
+ raw_a = a;
+ a = STBDS_HASH_TO_ARR(a,elemsize);
+
+ table = (stbds_hash_index *) stbds_header(a)->hash_table;
+
+ if (table == NULL || table->used_count >= table->used_count_threshold) {
+ stbds_hash_index *nt;
+ size_t slot_count;
+
+ slot_count = (table == NULL) ? STBDS_BUCKET_LENGTH : table->slot_count*2;
+ nt = stbds_make_hash_index(slot_count, table);
+ if (table)
+ STBDS_FREE(NULL, table);
+ else
+ nt->string.mode = mode >= STBDS_HM_STRING ? STBDS_SH_DEFAULT : 0;
+ stbds_header(a)->hash_table = table = nt;
+ STBDS_STATS(++stbds_hash_grow);
+ }
+
+ // we iterate hash table explicitly because we want to track if we saw a tombstone
+ {
+ size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed);
+ size_t step = STBDS_BUCKET_LENGTH;
+ size_t pos;
+ ptrdiff_t tombstone = -1;
+ stbds_hash_bucket *bucket;
+
+ // stored hash values are forbidden from being 0, so we can detect empty slots to early out quickly
+ if (hash < 2) hash += 2;
+
+ pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2);
+
+ for (;;) {
+ size_t limit, i;
+ STBDS_STATS(++stbds_hash_probes);
+ bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
+
+ // start searching from pos to end of bucket
+ for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) {
+ if (bucket->hash[i] == hash) {
+ if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
+ stbds_temp(a) = bucket->index[i];
+ if (mode >= STBDS_HM_STRING)
+ stbds_temp_key(a) = * (char **) ((char *) raw_a + elemsize*bucket->index[i] + keyoffset);
+ return STBDS_ARR_TO_HASH(a,elemsize);
+ }
+ } else if (bucket->hash[i] == 0) {
+ pos = (pos & ~STBDS_BUCKET_MASK) + i;
+ goto found_empty_slot;
+ } else if (tombstone < 0) {
+ if (bucket->index[i] == STBDS_INDEX_DELETED)
+ tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i);
+ }
+ }
+
+ // search from beginning of bucket to pos
+ limit = pos & STBDS_BUCKET_MASK;
+ for (i = 0; i < limit; ++i) {
+ if (bucket->hash[i] == hash) {
+ if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
+ stbds_temp(a) = bucket->index[i];
+ return STBDS_ARR_TO_HASH(a,elemsize);
+ }
+ } else if (bucket->hash[i] == 0) {
+ pos = (pos & ~STBDS_BUCKET_MASK) + i;
+ goto found_empty_slot;
+ } else if (tombstone < 0) {
+ if (bucket->index[i] == STBDS_INDEX_DELETED)
+ tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i);
+ }
+ }
+
+ // quadratic probing
+ pos += step;
+ step += STBDS_BUCKET_LENGTH;
+ pos &= (table->slot_count-1);
+ }
+ found_empty_slot:
+ if (tombstone >= 0) {
+ pos = tombstone;
+ --table->tombstone_count;
+ }
+ ++table->used_count;
+
+ {
+ ptrdiff_t i = (ptrdiff_t) stbds_arrlen(a);
+ // we want to do stbds_arraddn(1), but we can't use the macros since we don't have something of the right type
+ if ((size_t) i+1 > stbds_arrcap(a))
+ *(void **) &a = stbds_arrgrowf(a, elemsize, 1, 0);
+ raw_a = STBDS_ARR_TO_HASH(a,elemsize);
+
+ STBDS_ASSERT((size_t) i+1 <= stbds_arrcap(a));
+ stbds_header(a)->length = i+1;
+ bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
+ bucket->hash[pos & STBDS_BUCKET_MASK] = hash;
+ bucket->index[pos & STBDS_BUCKET_MASK] = i-1;
+ stbds_temp(a) = i-1;
+
+ switch (table->string.mode) {
+ case STBDS_SH_STRDUP: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_strdup((char*) key); break;
+ case STBDS_SH_ARENA: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_stralloc(&table->string, (char*)key); break;
+ case STBDS_SH_DEFAULT: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = (char *) key; break;
+ default: memcpy((char *) a + elemsize*i, key, keysize); break;
+ }
+ }
+ return STBDS_ARR_TO_HASH(a,elemsize);
+ }
+}
+
+void * stbds_shmode_func(size_t elemsize, int mode)
+{
+ void *a = stbds_arrgrowf(0, elemsize, 0, 1);
+ stbds_hash_index *h;
+ memset(a, 0, elemsize);
+ stbds_header(a)->length = 1;
+ stbds_header(a)->hash_table = h = (stbds_hash_index *) stbds_make_hash_index(STBDS_BUCKET_LENGTH, NULL);
+ h->string.mode = (unsigned char) mode;
+ return STBDS_ARR_TO_HASH(a,elemsize);
+}
+
+void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode)
+{
+ if (a == NULL) {
+ return 0;
+ } else {
+ stbds_hash_index *table;
+ void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
+ table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;
+ stbds_temp(raw_a) = 0;
+ if (table == 0) {
+ return a;
+ } else {
+ ptrdiff_t slot;
+ slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode);
+ if (slot < 0)
+ return a;
+ else {
+ stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
+ int i = slot & STBDS_BUCKET_MASK;
+ ptrdiff_t old_index = b->index[i];
+ ptrdiff_t final_index = (ptrdiff_t) stbds_arrlen(raw_a)-1-1; // minus one for the raw_a vs a, and minus one for 'last'
+ STBDS_ASSERT(slot < (ptrdiff_t) table->slot_count);
+ --table->used_count;
+ ++table->tombstone_count;
+ stbds_temp(raw_a) = 1;
+ STBDS_ASSERT(table->used_count >= 0);
+ //STBDS_ASSERT(table->tombstone_count < table->slot_count/4);
+ b->hash[i] = STBDS_HASH_DELETED;
+ b->index[i] = STBDS_INDEX_DELETED;
+
+ if (mode == STBDS_HM_STRING && table->string.mode == STBDS_SH_STRDUP)
+ STBDS_FREE(NULL, *(char**) ((char *) a+elemsize*old_index));
+
+ // if indices are the same, memcpy is a no-op, but back-pointer-fixup will fail, so skip
+ if (old_index != final_index) {
+ // swap delete
+ memmove((char*) a + elemsize*old_index, (char*) a + elemsize*final_index, elemsize);
+
+ // now find the slot for the last element
+ if (mode == STBDS_HM_STRING)
+ slot = stbds_hm_find_slot(a, elemsize, *(char**) ((char *) a+elemsize*old_index + keyoffset), keysize, keyoffset, mode);
+ else
+ slot = stbds_hm_find_slot(a, elemsize, (char* ) a+elemsize*old_index + keyoffset, keysize, keyoffset, mode);
+ STBDS_ASSERT(slot >= 0);
+ b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
+ i = slot & STBDS_BUCKET_MASK;
+ STBDS_ASSERT(b->index[i] == final_index);
+ b->index[i] = old_index;
+ }
+ stbds_header(raw_a)->length -= 1;
+
+ if (table->used_count < table->used_count_shrink_threshold && table->slot_count > STBDS_BUCKET_LENGTH) {
+ stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count>>1, table);
+ STBDS_FREE(NULL, table);
+ STBDS_STATS(++stbds_hash_shrink);
+ } else if (table->tombstone_count > table->tombstone_count_threshold) {
+ stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count , table);
+ STBDS_FREE(NULL, table);
+ STBDS_STATS(++stbds_hash_rebuild);
+ }
+
+ return a;
+ }
+ }
+ }
+ /* NOTREACHED */
+}
+
+static char *stbds_strdup(char *str)
+{
+ // to keep replaceable allocator simple, we don't want to use strdup.
+ // rolling our own also avoids problem of strdup vs _strdup
+ size_t len = strlen(str)+1;
+ char *p = (char*) STBDS_REALLOC(NULL, 0, len);
+ memmove(p, str, len);
+ return p;
+}
+
+#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MIN
+#define STBDS_STRING_ARENA_BLOCKSIZE_MIN 512u
+#endif
+#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MAX
+#define STBDS_STRING_ARENA_BLOCKSIZE_MAX (1u<<20)
+#endif
+
+char *stbds_stralloc(stbds_string_arena *a, char *str)
+{
+ char *p;
+ size_t len = strlen(str)+1;
+ if (len > a->remaining) {
+ // compute the next blocksize
+ size_t blocksize = a->block;
+
+ // size is 512, 512, 1024, 1024, 2048, 2048, 4096, 4096, etc., so that
+ // there are log(SIZE) allocations to free when we destroy the table
+ blocksize = (size_t) (STBDS_STRING_ARENA_BLOCKSIZE_MIN) << (blocksize>>1);
+
+ // if size is under 1M, advance to next blocktype
+ if (blocksize < (size_t)(STBDS_STRING_ARENA_BLOCKSIZE_MAX))
+ ++a->block;
+
+ if (len > blocksize) {
+ // if string is larger than blocksize, then just allocate the full size.
+ // note that we still advance string_block so block size will continue
+ // increasing, so e.g. if somebody only calls this with 1000-long strings,
+ // eventually the arena will start doubling and handling those as well
+ stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + len);
+ memmove(sb->storage, str, len);
+ if (a->storage) {
+ // insert it after the first element, so that we don't waste the space there
+ sb->next = a->storage->next;
+ a->storage->next = sb;
+ } else {
+ sb->next = 0;
+ a->storage = sb;
+ a->remaining = 0; // this is redundant, but good for clarity
+ }
+ return sb->storage;
+ } else {
+ stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + blocksize);
+ sb->next = a->storage;
+ a->storage = sb;
+ a->remaining = blocksize;
+ }
+ }
+
+ STBDS_ASSERT(len <= a->remaining);
+ p = a->storage->storage + a->remaining - len;
+ a->remaining -= len;
+ memmove(p, str, len);
+ return p;
+}
+
+void stbds_strreset(stbds_string_arena *a)
+{
+ stbds_string_block *x,*y;
+ x = a->storage;
+ while (x) {
+ y = x->next;
+ STBDS_FREE(NULL, x);
+ x = y;
+ }
+ memset(a, 0, sizeof(*a));
+}
+
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// UNIT TESTS
+//
+
+#ifdef STBDS_UNIT_TESTS
+#include <stdio.h>
+#ifdef STBDS_ASSERT_WAS_UNDEFINED
+#undef STBDS_ASSERT
+#endif
+#ifndef STBDS_ASSERT
+#define STBDS_ASSERT assert
+#include <assert.h>
+#endif
+
+typedef struct { int key,b,c,d; } stbds_struct;
+typedef struct { int key[2],b,c,d; } stbds_struct2;
+
+static char buffer[256];
+char *strkey(int n)
+{
+#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)
+ sprintf_s(buffer, sizeof(buffer), "test_%d", n);
+#else
+ sprintf(buffer, "test_%d", n);
+#endif
+ return buffer;
+}
+
+void stbds_unit_tests(void)
+{
+#if defined(_MSC_VER) && _MSC_VER <= 1200 && defined(__cplusplus)
+ // VC6 C++ doesn't like the template<> trick on unnamed structures, so do nothing!
+ STBDS_ASSERT(0);
+#else
+ const int testsize = 100000;
+ const int testsize2 = testsize/20;
+ int *arr=NULL;
+ struct { int key; int value; } *intmap = NULL;
+ struct { char *key; int value; } *strmap = NULL, s;
+ struct { stbds_struct key; int value; } *map = NULL;
+ stbds_struct *map2 = NULL;
+ stbds_struct2 *map3 = NULL;
+ stbds_string_arena sa = { 0 };
+ int key3[2] = { 1,2 };
+ ptrdiff_t temp;
+
+ int i,j;
+
+ STBDS_ASSERT(arrlen(arr)==0);
+ for (i=0; i < 20000; i += 50) {
+ for (j=0; j < i; ++j)
+ arrpush(arr,j);
+ arrfree(arr);
+ }
+
+ for (i=0; i < 4; ++i) {
+ arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
+ arrdel(arr,i);
+ arrfree(arr);
+ arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
+ arrdelswap(arr,i);
+ arrfree(arr);
+ }
+
+ for (i=0; i < 5; ++i) {
+ arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
+ stbds_arrins(arr,i,5);
+ STBDS_ASSERT(arr[i] == 5);
+ if (i < 4)
+ STBDS_ASSERT(arr[4] == 4);
+ arrfree(arr);
+ }
+
+ i = 1;
+ STBDS_ASSERT(hmgeti(intmap,i) == -1);
+ hmdefault(intmap, -2);
+ STBDS_ASSERT(hmgeti(intmap, i) == -1);
+ STBDS_ASSERT(hmget (intmap, i) == -2);
+ for (i=0; i < testsize; i+=2)
+ hmput(intmap, i, i*5);
+ for (i=0; i < testsize; i+=1) {
+ if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 );
+ else STBDS_ASSERT(hmget(intmap, i) == i*5);
+ if (i & 1) STBDS_ASSERT(hmget_ts(intmap, i, temp) == -2 );
+ else STBDS_ASSERT(hmget_ts(intmap, i, temp) == i*5);
+ }
+ for (i=0; i < testsize; i+=2)
+ hmput(intmap, i, i*3);
+ for (i=0; i < testsize; i+=1)
+ if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 );
+ else STBDS_ASSERT(hmget(intmap, i) == i*3);
+ for (i=2; i < testsize; i+=4)
+ hmdel(intmap, i); // delete half the entries
+ for (i=0; i < testsize; i+=1)
+ if (i & 3) STBDS_ASSERT(hmget(intmap, i) == -2 );
+ else STBDS_ASSERT(hmget(intmap, i) == i*3);
+ for (i=0; i < testsize; i+=1)
+ hmdel(intmap, i); // delete the rest of the entries
+ for (i=0; i < testsize; i+=1)
+ STBDS_ASSERT(hmget(intmap, i) == -2 );
+ hmfree(intmap);
+ for (i=0; i < testsize; i+=2)
+ hmput(intmap, i, i*3);
+ hmfree(intmap);
+
+ #if defined(__clang__) || defined(__GNUC__)
+ #ifndef __cplusplus
+ intmap = NULL;
+ hmput(intmap, 15, 7);
+ hmput(intmap, 11, 3);
+ hmput(intmap, 9, 5);
+ STBDS_ASSERT(hmget(intmap, 9) == 5);
+ STBDS_ASSERT(hmget(intmap, 11) == 3);
+ STBDS_ASSERT(hmget(intmap, 15) == 7);
+ #endif
+ #endif
+
+ for (i=0; i < testsize; ++i)
+ stralloc(&sa, strkey(i));
+ strreset(&sa);
+
+ {
+ s.key = "a", s.value = 1;
+ shputs(strmap, s);
+ STBDS_ASSERT(*strmap[0].key == 'a');
+ STBDS_ASSERT(strmap[0].key == s.key);
+ STBDS_ASSERT(strmap[0].value == s.value);
+ shfree(strmap);
+ }
+
+ {
+ s.key = "a", s.value = 1;
+ sh_new_strdup(strmap);
+ shputs(strmap, s);
+ STBDS_ASSERT(*strmap[0].key == 'a');
+ STBDS_ASSERT(strmap[0].key != s.key);
+ STBDS_ASSERT(strmap[0].value == s.value);
+ shfree(strmap);
+ }
+
+ {
+ s.key = "a", s.value = 1;
+ sh_new_arena(strmap);
+ shputs(strmap, s);
+ STBDS_ASSERT(*strmap[0].key == 'a');
+ STBDS_ASSERT(strmap[0].key != s.key);
+ STBDS_ASSERT(strmap[0].value == s.value);
+ shfree(strmap);
+ }
+
+ for (j=0; j < 2; ++j) {
+ STBDS_ASSERT(shgeti(strmap,"foo") == -1);
+ if (j == 0)
+ sh_new_strdup(strmap);
+ else
+ sh_new_arena(strmap);
+ STBDS_ASSERT(shgeti(strmap,"foo") == -1);
+ shdefault(strmap, -2);
+ STBDS_ASSERT(shgeti(strmap,"foo") == -1);
+ for (i=0; i < testsize; i+=2)
+ shput(strmap, strkey(i), i*3);
+ for (i=0; i < testsize; i+=1)
+ if (i & 1) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
+ else STBDS_ASSERT(shget(strmap, strkey(i)) == i*3);
+ for (i=2; i < testsize; i+=4)
+ shdel(strmap, strkey(i)); // delete half the entries
+ for (i=0; i < testsize; i+=1)
+ if (i & 3) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
+ else STBDS_ASSERT(shget(strmap, strkey(i)) == i*3);
+ for (i=0; i < testsize; i+=1)
+ shdel(strmap, strkey(i)); // delete the rest of the entries
+ for (i=0; i < testsize; i+=1)
+ STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
+ shfree(strmap);
+ }
+
+ {
+ struct { char *key; char value; } *hash = NULL;
+ char name[4] = "jen";
+ shput(hash, "bob" , 'h');
+ shput(hash, "sally" , 'e');
+ shput(hash, "fred" , 'l');
+ shput(hash, "jen" , 'x');
+ shput(hash, "doug" , 'o');
+
+ shput(hash, name , 'l');
+ shfree(hash);
+ }
+
+ for (i=0; i < testsize; i += 2) {
+ stbds_struct s = { i,i*2,i*3,i*4 };
+ hmput(map, s, i*5);
+ }
+
+ for (i=0; i < testsize; i += 1) {
+ stbds_struct s = { i,i*2,i*3 ,i*4 };
+ stbds_struct t = { i,i*2,i*3+1,i*4 };
+ if (i & 1) STBDS_ASSERT(hmget(map, s) == 0);
+ else STBDS_ASSERT(hmget(map, s) == i*5);
+ if (i & 1) STBDS_ASSERT(hmget_ts(map, s, temp) == 0);
+ else STBDS_ASSERT(hmget_ts(map, s, temp) == i*5);
+ //STBDS_ASSERT(hmget(map, t.key) == 0);
+ }
+
+ for (i=0; i < testsize; i += 2) {
+ stbds_struct s = { i,i*2,i*3,i*4 };
+ hmputs(map2, s);
+ }
+ hmfree(map);
+
+ for (i=0; i < testsize; i += 1) {
+ stbds_struct s = { i,i*2,i*3,i*4 };
+ stbds_struct t = { i,i*2,i*3+1,i*4 };
+ if (i & 1) STBDS_ASSERT(hmgets(map2, s.key).d == 0);
+ else STBDS_ASSERT(hmgets(map2, s.key).d == i*4);
+ //STBDS_ASSERT(hmgetp(map2, t.key) == 0);
+ }
+ hmfree(map2);
+
+ for (i=0; i < testsize; i += 2) {
+ stbds_struct2 s = { { i,i*2 }, i*3,i*4, i*5 };
+ hmputs(map3, s);
+ }
+ for (i=0; i < testsize; i += 1) {
+ stbds_struct2 s = { { i,i*2}, i*3, i*4, i*5 };
+ stbds_struct2 t = { { i,i*2}, i*3+1, i*4, i*5 };
+ if (i & 1) STBDS_ASSERT(hmgets(map3, s.key).d == 0);
+ else STBDS_ASSERT(hmgets(map3, s.key).d == i*5);
+ //STBDS_ASSERT(hmgetp(map3, t.key) == 0);
+ }
+#endif
+}
+#endif
+
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2019 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/
+++ /dev/null
-#include "astnodes.h"
-#include "parser.h"
-#include "utils.h"
-
-static const char* ast_node_names[] = {
- "ERROR",
- "PACKAGE",
- "INCLUDE FILE",
- "INCLUDE FOLDER",
- "INCLUDE ALL IN FOLDER",
- "INCLUDE LIBRARY PATH",
- "MEMORY RESERVATION",
-
- "BINDING",
- "ALIAS",
- "INJECTION",
- "FUNCTION",
- "OVERLOADED_FUNCTION",
- "POLYMORPHIC PROC",
- "POLYMORPH QUERY",
- "INTERFACE",
- "CONSTRAINT",
- "CONSTRAINT SENTITEL",
- "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",
- "DISTINCT TYPE",
- "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",
- "CASE",
-
- "SOLIDIFY",
- "STATIC IF",
- "STATIC ERROR",
- "ADD OVERLOAD",
- "OPERATOR OVERLOAD",
- "EXPORT",
- "DEFINED",
- "TAG",
- "INIT",
- "LIBRARY",
- "REMOVE",
- "CALL SITE",
-
- "CODE BLOCK",
- "DIRECTIVE INSERT",
- "MACRO",
- "DO BLOCK",
-
- "FOREIGN BLOCK",
- "ZERO VALUE",
-
- "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",
- "Failed",
-};
-
-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",
- "Enum Value",
- "Type Alias",
- "Memory Reservation Type",
- "Use",
- "Interface",
- "Constraint Check",
- "Polymorphic Proc",
- "Polymorph Query",
- "Foreign Block",
- "Macro",
- "Foreign_Function Header",
- "Temporary Function Header",
- "Function Header",
- "Global Header",
- "Process Directive",
- "Struct Member Default",
- "Memory Reservation",
- "Expression",
- "Global",
- "Overloaded_Function",
- "Function",
-};
-
-AstNumLit* ast_reduce_type_compare(bh_allocator a, AstBinaryOp* node) {
- AstType* left = (AstType *) ast_reduce(a, node->left);
- AstType* right = (AstType *) ast_reduce(a, node->right);
-
- Type* left_type = type_build_from_ast(context.ast_alloc, left);
- Type* right_type = type_build_from_ast(context.ast_alloc, right);
-
- 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 = (AstType *) &basic_type_bool;
- res->type = &basic_types[Basic_Kind_Bool];
- res->next = node->next;
-
- switch (node->operation) {
- case Binary_Op_Equal: res->value.l = left_type->id == right_type->id; break;
- case Binary_Op_Not_Equal: res->value.l = left_type->id != right_type->id; break;
- default: assert(("Bad case in ast_reduce_type_compare", 0));
- }
-
- return res;
-}
-
-#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 (node_is_type((AstNode *) left) && node_is_type((AstNode *) right)) {
- if (node->operation == Binary_Op_Equal || node->operation == Binary_Op_Not_Equal) {
- return (AstNumLit *) ast_reduce_type_compare(a, node);
- }
- }
-
- 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* to_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, to_type)) return 1;
- if (!type_is_numeric(to_type)) return 0;
-
- Type *type = to_type;
- if (type->kind == Type_Kind_Enum) type = type->Enum.backing;
- if (type->kind == Type_Kind_Distinct) type = type->Distinct.base_type;
-
- 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 = to_type;
- return 1;
- }
- switch (type->Basic.size) {
- case 1: if (value <= 255) {
- num->type = to_type;
- return 1;
- }
- case 2: if (value <= 65535) {
- num->type = to_type;
- return 1;
- }
- case 4: if (value <= 4294967295) {
- num->type = to_type;
- return 1;
- }
- }
-
- onyx_report_error(num->token->pos, Error_Critical, "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 = to_type;
- return 1;
- } break;
- case 2: if (-32768ll <= value && value <= 32767ll) {
- num->value.i = (i32) value;
- num->type = to_type;
- return 1;
- } break;
- case 4: if (-2147483648ll <= value && value <= 2147483647ll) {
- num->value.i = (i32) value;
- num->type = to_type;
- return 1;
- } break;
- case 8: { num->type = to_type;
- return 1;
- } break;
- }
-
- onyx_report_error(num->token->pos, Error_Critical, "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, Error_Critical, "Integer '%l' does not fit in 32-bit float exactly.", num->value.l);
- return 0;
- }
-
- num->type = to_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, Error_Critical, "Integer '%l' does not fit in 64-bit float exactly.", num->value.l);
- return 0;
- }
-
- num->type = to_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 = to_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 = to_type;
- return 1;
- }
- }
-
- return 0;
-}
-
-// TODO: This function should be able return a "yield" condition. There
-// are a couple cases that need to yield in order to be correct, like
-// polymorphic functions with a typeof for the return type.
-TypeMatch unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent) {
- AstTyped* node = *pnode;
- if (type == NULL) return TYPE_MATCH_FAILED;
- if (node == NULL) return TYPE_MATCH_FAILED;
-
- if (node->kind == Ast_Kind_Struct_Literal && (node->type_node == NULL && node->type == NULL)) {
- if (node->entity != NULL) return TYPE_MATCH_SUCCESS;
- if (type->kind == Type_Kind_VarArgs) type = type->VarArgs.elem;
-
- //
- // If the structure literal has arguments, and the type is not constructable
- // using a struct literal, then they cannot be unified. However, if no arguments
- // are given, e.g. .{}, then any type should be matched, as that is the universal
- // zero-value.
- if (!type_is_sl_constructable(type)) {
- AstStructLiteral *sl = (AstStructLiteral *) node;
- if (bh_arr_length(sl->args.values) != 0 || bh_arr_length(sl->args.named_values) != 0) {
- return TYPE_MATCH_FAILED;
- }
- }
-
- // If this shouldn't make permanent changes and submit entities,
- // just assume that it works and don't submit the entities.
- if (!permanent) return TYPE_MATCH_SUCCESS;
-
- node->type = type;
-
- add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
- return TYPE_MATCH_SUCCESS;
- }
-
- if (node->kind == Ast_Kind_Array_Literal && node->type == NULL) {
- if (node->entity != NULL) return TYPE_MATCH_SUCCESS;
-
- // If this shouldn't make permanent changes and submit entities,
- // just assume that it works and don't submit the entities.
- if (!permanent) return TYPE_MATCH_SUCCESS;
-
- Type* array_type=NULL;
- switch (type->kind) {
- case Type_Kind_Array: array_type = type; break;
- case Type_Kind_Slice: {
- Type* elem_type = type->Slice.elem;
- AstArrayLiteral* al = (AstArrayLiteral *) node;
- array_type = type_make_array(context.ast_alloc, elem_type, bh_arr_length(al->values));
-
- *pnode = (AstTyped *) make_cast(context.ast_alloc, node, type);
-
- break;
- }
-
- default: assert(0);
- }
-
- node->type = array_type;
- node->flags |= Ast_Flag_Array_Literal_Typed;
-
- add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
- return TYPE_MATCH_SUCCESS;
- }
-
- 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) {
- if (context.cycle_detected) {
- token_toggle_end(node->token);
- char *closest = find_closest_symbol_in_node((AstNode *) ast_type, node->token->text);
- token_toggle_end(node->token);
-
- if (closest) {
- onyx_report_error(node->token->pos, Error_Critical, "'%b' does not exist in '%s'. Did you mean '%s'?",
- node->token->text, node->token->length,
- type_get_name(type),
- closest);
- return TYPE_MATCH_FAILED;
- }
- }
-
- return TYPE_MATCH_YIELD;
- }
-
- if (permanent) *pnode = (AstTyped *) resolved;
- return TYPE_MATCH_SUCCESS;
- }
-
- if (node->kind == Ast_Kind_Overloaded_Function) {
- AstTyped* func = find_matching_overload_by_type(((AstOverloadedFunction *) node)->overloads, type);
- if (func == NULL) return TYPE_MATCH_FAILED;
- if (func == (AstTyped *) &node_that_signals_a_yield) return TYPE_MATCH_YIELD;
-
- // 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((AstFunction *) node, PPLM_By_Function_Type, type, node->token);
- if (func == NULL) return TYPE_MATCH_FAILED;
- if (func == (AstFunction *) &node_that_signals_a_yield) return TYPE_MATCH_YIELD;
-
- *pnode = (AstTyped *) func;
- node = *pnode;
- }
-
- // HACK: NullProcHack
- // The null_proc matches any procedure, and because of that, will cause a runtime error if you
- // try to call it.
- if (type->kind == Type_Kind_Function && (node->flags & Ast_Flag_Proc_Is_Null) != 0) return TYPE_MATCH_SUCCESS;
-
- // The normal case where everything works perfectly.
- Type* node_type = get_expression_type(node);
- if (types_are_compatible(node_type, type)) return TYPE_MATCH_SUCCESS;
-
- Type* any_type = type_build_from_ast(context.ast_alloc, builtin_any_type);
- if (any_type == NULL) return TYPE_MATCH_YIELD;
- i64 any_id = any_type->id;
-
- if (node_type && node_type->id != any_id && type->id == any_id) return TYPE_MATCH_SUCCESS;
-
- // Here are some of the ways you can unify a node with a type if the type of the
- // node does not match the given type:
- //
- // If the nodes type is a function type and that function has an automatic return
- // value placeholder, fill in that placeholder with the actual type.
- // :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 TYPE_MATCH_SUCCESS;
- }
-
- // If the node is an auto cast (~~) node, then check to see if the cast is legal
- // to the destination type, and if it is change the type to cast to.
- if (node_is_auto_cast((AstNode *) node)) {
- char* dummy;
- Type* from_type = get_expression_type(((AstUnaryOp *) node)->expr);
- if (!from_type || !cast_is_legal(from_type, type, &dummy)) {
- return TYPE_MATCH_FAILED;
-
- } else {
- if (permanent) ((AstUnaryOp *) node)->type = type;
- return TYPE_MATCH_SUCCESS;
- }
- }
-
- // If the destination type is a slice, then automatically convert arrays, dynamic
- // arrays, and var args, if they are the same type. This is big convenience feature
- // that makes working with arrays much easier.
- // [N] T -> [] T
- // [..] T -> [] T
- // ..T -> [] T
- else if (node_type && type->kind == Type_Kind_Slice) {
- if (node_type->kind == Type_Kind_Array || node_type->kind == Type_Kind_DynArray || node_type->kind == Type_Kind_VarArgs) {
- char* dummy;
- b32 legal = cast_is_legal(node_type, type, &dummy);
- if (permanent && legal) {
- *pnode = (AstTyped *) make_cast(context.ast_alloc, node, type);
- }
-
- return legal ? TYPE_MATCH_SUCCESS : TYPE_MATCH_FAILED;
- }
- }
-
- // If the node is a numeric literal, try to convert it to the destination type.
- else if (node->kind == Ast_Kind_NumLit) {
- if (convert_numlit_to_type((AstNumLit *) node, type)) return TYPE_MATCH_SUCCESS;
- }
-
- // If the node is a compound expression, and it doesn't have a type created,
- // recursive call this function with the individual components of the compound
- // expression.
- else if (node->kind == Ast_Kind_Compound) {
- if (type->kind != Type_Kind_Compound) return TYPE_MATCH_FAILED;
-
- AstCompound* compound = (AstCompound *) node;
-
- u32 expr_count = bh_arr_length(compound->exprs);
- if (expr_count != type->Compound.count) return TYPE_MATCH_FAILED;
-
- fori (i, 0, (i64) expr_count) {
- TypeMatch tm = unify_node_and_type_(&compound->exprs[i], type->Compound.types[i], permanent);
- if (tm != TYPE_MATCH_SUCCESS) {
- return tm;
- }
- }
-
- compound->type = type_build_compound_type(context.ast_alloc, compound);
-
- return TYPE_MATCH_SUCCESS;
- }
-
- else if (node->kind == Ast_Kind_If_Expression) {
- AstIfExpression* if_expr = (AstIfExpression *) node;
-
- TypeMatch true_success = unify_node_and_type_(&if_expr->true_expr, type, permanent);
- TypeMatch false_success = unify_node_and_type_(&if_expr->false_expr, type, permanent);
-
- if (true_success == TYPE_MATCH_SUCCESS && false_success == TYPE_MATCH_SUCCESS) {
- if (permanent) if_expr->type = type;
- return TYPE_MATCH_SUCCESS;
-
- } else if (true_success == TYPE_MATCH_FAILED || false_success == TYPE_MATCH_FAILED) {
- return TYPE_MATCH_FAILED;
-
- } else {
- return TYPE_MATCH_YIELD;
- }
- }
-
- else if (node->kind == Ast_Kind_Alias) {
- AstAlias* alias = (AstAlias *) node;
- return unify_node_and_type_(&alias->alias, type, permanent);
- }
-
- else if (node->kind == Ast_Kind_Address_Of) {
- AstAddressOf *address_of = (AstAddressOf *) node;
- if (address_of->can_be_removed) {
- if (!permanent) {
- return unify_node_and_type_(&address_of->expr, type, permanent);
-
- } else {
- *pnode = (AstTyped *) address_of->expr;
- return unify_node_and_type_(pnode, type, permanent);
- }
- }
- }
-
- else if (node->kind == Ast_Kind_Zero_Value) {
- if (node_type == NULL) {
- node->type = type;
- return TYPE_MATCH_SUCCESS; // Shouldn't this be on the next line? And have node_type == node->type checked?
- }
- }
-
- return TYPE_MATCH_FAILED;
-}
-
-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);
- }
-
- node->type = type_build_compound_type(context.ast_alloc, (AstCompound *) node);
- return node->type;
- }
-
- 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);
- unify_node_and_type(&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->kind == Ast_Kind_Array_Literal && node->type == NULL) {
- AstArrayLiteral* al = (AstArrayLiteral *) node;
- Type* elem_type = &basic_types[Basic_Kind_Void];
- if (bh_arr_length(al->values) > 0) {
- elem_type = resolve_expression_type(al->values[0]);
- }
-
- if (elem_type) {
- node->type = type_make_array(context.ast_alloc, elem_type, bh_arr_length(al->values));
- node->flags |= Ast_Flag_Array_Literal_Typed;
-
- if (node->entity == NULL) {
- add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
- }
- }
- }
-
- if (node->kind == Ast_Kind_Struct_Literal && node->type == NULL) {
- AstStructLiteral* sl = (AstStructLiteral *) node;
- if (sl->stnode || sl->type_node) return NULL;
-
- // If values without names are given to a struct literal without
- // a type, then we cannot implicitly build the type of the struct
- // literal, as the name of every member cannot be known. Maybe we
- // could implicitly do something like _1, _2, ... for the members
- // that we not given names?
- if (bh_arr_length(sl->args.values) > 0) {
- return NULL;
- }
-
- sl->type = type_build_implicit_type_of_struct_literal(context.ast_alloc, sl);
- if (sl->type) {
- add_entities_for_node(NULL, (AstNode *) sl, NULL, NULL);
- }
- }
-
- // If polymorphic procedures HAVE to have a type, most likely
- // because they are part of a `typeof` expression, they are
- // assigned a void type. This is cleared before the procedure
- // is solidified.
- if (node->kind == Ast_Kind_Polymorphic_Proc) {
- node->type = &basic_types[Basic_Kind_Void];
- }
-
- if (node->kind == Ast_Kind_Macro) {
- return resolve_expression_type((AstTyped *) ((AstMacro *) node)->body);
- }
-
- 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) {
- b32 big = bh_abs(((AstNumLit *) node)->value.l) >= (1ull << 32);
- b32 unsign = ((AstNumLit *) node)->was_hex_literal;
-
- if ( big && !unsign) convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_I64]);
- else if ( big && unsign) convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_U64]);
- else if (!big && !unsign) convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_I32]);
- else if (!big && unsign) convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_U32]);
- }
- 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, b32 *is_valid) {
- if (!node) return 0;
-
- resolve_expression_type(node);
-
- if (is_valid) *is_valid = 1;
-
- 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, is_valid);
- }
-
- 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, is_valid);
- }
-
- if (node->kind == Ast_Kind_Enum_Value) {
- return get_expression_integer_value(((AstEnumValue *) node)->value, is_valid);
- }
-
- if (node_is_type((AstNode*) node)) {
- Type* type = type_build_from_ast(context.ast_alloc, (AstType *) node);
- if (type) return type->id;
- }
-
- if (is_valid) *is_valid = 0;
- return 0;
-}
-
-char *get_expression_string_value(AstTyped* node, b32 *out_is_valid) {
- resolve_expression_type(node);
-
- if (out_is_valid) *out_is_valid = 1;
-
- if (node->kind == Ast_Kind_StrLit) {
- AstStrLit *str = (AstStrLit *) node;
-
- // CLEANUP: Maybe this should allocate on the heap?
- // I guess if in all cases the memory is allocated on the heap,
- // then the caller can free the memory.
- i8* strdata = bh_alloc_array(global_heap_allocator, i8, str->token->length + 1);
- i32 length = string_process_escape_seqs(strdata, str->token->text, str->token->length);
- strdata[length] = '\0';
-
- return strdata;
- }
-
- if (node->kind == Ast_Kind_Alias) {
- return get_expression_string_value(((AstAlias *) node)->alias, out_is_valid);
- }
-
- if (out_is_valid) *out_is_valid = 0;
- return NULL;
-}
-
-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_->id == to_->id) return 1;
-
- 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;
- }
-
- // CLEANUP: These error messages should be a lot better and actually
- // provide the types of the things in question.
- if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
- if (!types_are_compatible(to->Slice.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_DynArray) {
- //if (!types_are_compatible(to->Slice.elem, from->DynArray.elem)) {
- if (type_size_of(to->Slice.elem) != type_size_of(from->DynArray.elem)) {
- *err_msg = "Dynmaic array to slice cast is not valid here because the types are different sizes.";
- return 0;
- } else {
- return 1;
- }
- }
-
- if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_VarArgs) {
- if (!types_are_compatible(to->Slice.elem, from->VarArgs.elem)) {
- *err_msg = "Variadic argument to slice cast is not valid here because the types are different.";
- return 0;
- } else {
- return 1;
- }
- }
-
- if (to->kind == Type_Kind_Distinct) {
- if (!types_are_compatible(to->Distinct.base_type, from)) {
- // :BadErrorMessage
- *err_msg = "Cannot convert to a distinct type using the wrong base type.";
- return 0;
- } else {
- return 1;
- }
- }
-
- if (from->kind == Type_Kind_Distinct) {
- if (!types_are_compatible(from->Distinct.base_type, to)) {
- // :BadErrorMessage
- *err_msg = "Cannot convert from a distinct type to the wrong destination type.";
- return 0;
- } else {
- return 1;
- }
- }
-
- if (from->kind == Type_Kind_Slice || to->kind == Type_Kind_Slice) {
- if ((from->kind != Type_Kind_Slice || to->kind != Type_Kind_Slice)
- || to->Slice.elem->kind != Type_Kind_Pointer || from->Slice.elem->kind != Type_Kind_Pointer
- || !types_are_compatible(from->Slice.elem, to->Slice.elem)) {
- *err_msg = "Cannot only cast between slice types when both are a slice of compatible pointers.";
- return 0;
- } else {
- return 1;
- }
- }
-
- if (from->kind == Type_Kind_DynArray || to->kind == Type_Kind_DynArray) {
- *err_msg = "Cannot cast to or from a dynamic array.";
- return 0;
- }
-
- if (to->kind == Type_Kind_Function) {
- *err_msg = "Cannot cast to a function.";
- return 0;
- }
-
- if ( (type_is_simd(to) && !type_is_simd(from))
- || (!type_is_simd(to) && type_is_simd(from))) {
- *err_msg = "Can only perform a SIMD cast between SIMD types.";
- return 0;
- }
-
- if (from->kind == Type_Kind_Basic && from->Basic.kind == Basic_Kind_Void) {
- *err_msg = "Cannot cast from void.";
- return 0;
- }
- i32 fromidx = -1, toidx = -1;
- if (from->Basic.flags & Basic_Flag_Pointer || from->kind == Type_Kind_Array) {
- fromidx = 10;
- }
- else if (from->Basic.flags & Basic_Flag_Integer) {
- b32 unsign = (from->Basic.flags & Basic_Flag_Unsigned) != 0;
-
- fromidx = log2_dumb(from->Basic.size) * 2 + unsign;
- }
- else if (from->Basic.flags & Basic_Flag_Float) {
- if (from->Basic.size == 4) fromidx = 8;
- else if (from->Basic.size == 8) fromidx = 9;
- }
- else if (from->Basic.flags & Basic_Flag_Boolean) {
- fromidx = 0;
- }
- else if (from->Basic.flags & Basic_Flag_Type_Index) {
- fromidx = 11;
- }
-
- if (to->Basic.flags & Basic_Flag_Pointer || to->kind == Type_Kind_Array) {
- toidx = 10;
- }
- else if (to->Basic.flags & Basic_Flag_Integer) {
- b32 unsign = (to->Basic.flags & Basic_Flag_Unsigned) != 0;
-
- toidx = log2_dumb(to->Basic.size) * 2 + unsign;
- }
- else if (to->Basic.flags & Basic_Flag_Float) {
- if (to->Basic.size == 4) toidx = 8;
- else if (to->Basic.size == 8) toidx = 9;
- }
- else if (to->Basic.flags & Basic_Flag_Boolean) {
- toidx = 0;
- }
- else if (to->Basic.flags & Basic_Flag_Type_Index) {
- toidx = 11;
- }
-
- if (fromidx != -1 && toidx != -1) {
- if (!cast_legality[fromidx][toidx]) {
- *err_msg = bh_aprintf(global_heap_allocator, "Cast from '%s' to '%s' is not allowed.", type_get_name(from_), type_get_name(to_));
- return 0;
- }
- } else {
- *err_msg = bh_aprintf(global_heap_allocator, "Cast from '%s' to '%s' is not allowed.", type_get_name(from_), type_get_name(to_));
- return 0;
- }
-
- *err_msg = NULL;
- return 1;
-}
-
-char* get_function_name(AstFunction* func) {
- if (func->kind != Ast_Kind_Function) return "<procedure>";
-
- if (func->name != NULL) return func->name;
-
- if (func->exported_name != NULL) {
- return bh_aprintf(global_scratch_allocator,
- "EXPORTED:%b",
- func->exported_name->text,
- func->exported_name->length);
- }
-
- return "<anonymous procedure>";
-}
-
-AstNode* strip_aliases(AstNode* n) {
- if (n == NULL) return n;
-
- while (n->kind == Ast_Kind_Alias) n = (AstNode *) ((AstAlias *) n)->alias;
-
- return n;
-}
-
-AstNumLit* make_bool_literal(bh_allocator a, b32 b) {
- AstNumLit* bl = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
- bl->flags |= Ast_Flag_Comptime;
- bl->type_node = (AstType *) &basic_type_bool;
-
- bl->value.i = b ? 1 : 0;
- return bl;
-}
-
-AstNumLit* make_int_literal(bh_allocator a, i64 i) {
- AstNumLit* num = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
- num->flags |= Ast_Flag_Comptime;
-
- if (bh_abs(i) >= ((u64) 1 << 32))
- num->type_node = (AstType *) &basic_type_i64;
- else
- num->type_node = (AstType *) &basic_type_i32;
-
- num->value.l = i;
- return num;
-}
-
-AstNumLit* make_float_literal(bh_allocator a, f64 d) {
- // NOTE: Use convert_numlit_to_type to make this a concrete float
- AstNumLit* num = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
- num->flags |= Ast_Flag_Comptime;
- num->type_node = (AstType *) &basic_type_float_unsized;
- num->value.d = d;
- return num;
-}
-
-AstRangeLiteral* make_range_literal(bh_allocator a, AstTyped* low, AstTyped* high) {
- AstRangeLiteral* rl = onyx_ast_node_new(a, sizeof(AstRangeLiteral), Ast_Kind_Range_Literal);
- rl->type = builtin_range_type_type;
- rl->low = low;
- rl->high = high;
- return rl;
-}
-
-AstBinaryOp* make_binary_op(bh_allocator a, BinaryOp operation, AstTyped* left, AstTyped* right) {
- AstBinaryOp* binop_node = onyx_ast_node_new(a, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
- binop_node->left = left;
- binop_node->right = right;
- binop_node->operation = operation;
- return binop_node;
-}
-
-AstArgument* make_argument(bh_allocator a, AstTyped* value) {
- AstArgument* arg = onyx_ast_node_new(a, sizeof(AstArgument), Ast_Kind_Argument);
- if (value->token) arg->token = value->token;
- arg->value = value;
- arg->type = value->type;
- arg->next = NULL;
- arg->va_kind = VA_Kind_Not_VA;
- return arg;
-}
-
-AstFieldAccess* make_field_access(bh_allocator a, AstTyped* node, char* field) {
- AstFieldAccess* fa = onyx_ast_node_new(a, sizeof(AstFieldAccess), Ast_Kind_Field_Access);
- if (node->token) fa->token = node->token;
- fa->field = field;
- fa->expr = node;
-
- return fa;
-}
-
-AstAddressOf* make_address_of(bh_allocator a, AstTyped* node) {
- AstAddressOf* ao = onyx_ast_node_new(a, sizeof(AstAddressOf), Ast_Kind_Address_Of);
- if (node->token) ao->token = node->token;
- ao->expr = node;
-
- return ao;
-}
-
-AstLocal* make_local(bh_allocator a, OnyxToken* token, AstType* type_node) {
- AstLocal* local = onyx_ast_node_new(a, sizeof(AstLocal), Ast_Kind_Local);
- local->token = token;
- local->type_node = type_node;
-
- return local;
-}
-
-AstNode* make_symbol(bh_allocator a, OnyxToken* sym) {
- AstNode* symbol = onyx_ast_node_new(a, sizeof(AstNode), Ast_Kind_Symbol);
- symbol->token = sym;
- return symbol;
-}
-
-AstUnaryOp* make_cast(bh_allocator a, AstTyped* expr, Type* to) {
- AstUnaryOp* cast = onyx_ast_node_new(a, sizeof(AstUnaryOp), Ast_Kind_Unary_Op);
- cast->token = expr->token;
- cast->operation = Unary_Op_Cast;
- cast->expr = expr;
- cast->type = to;
- return cast;
-}
-
-AstZeroValue* make_zero_value(bh_allocator a, OnyxToken* token, Type* type) {
- AstZeroValue* zero_value = onyx_ast_node_new(a, sizeof(AstZeroValue), Ast_Kind_Zero_Value);
- zero_value->token = token;
- zero_value->flags |= Ast_Flag_Comptime;
- zero_value->type = type;
- return zero_value;
-}
-
-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;
- }
-
- args->used_argument_count = -1;
-}
-
-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->used_argument_count = -1;
- dest->named_values = src->named_values;
-
- bh_arr_grow(dest->values, (u32) bh_arr_length(src->values));
- bh_arr_set_length(dest->values, (u32) 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->used_argument_count = -1;
- 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->used_argument_count = -1;
- 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--;
- }
-}
-
-void arguments_clear_baked_flags(Arguments* args) {
- bh_arr_each(AstTyped *, arg, args->values) {
- if ((*arg)->kind == Ast_Kind_Argument) {
- ((AstArgument *) *arg)->is_baked = 0;
- }
- }
-
- bh_arr_each(AstNamedValue *, arg, args->named_values) {
- if ((*arg)->value->kind == Ast_Kind_Argument) {
- ((AstArgument *) (*arg)->value)->is_baked = 0;
- }
- }
-}
-
-// 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, NULL);
-
- return value != 0;
-}
-
-AstPolyCallType* convert_call_to_polycall(AstCall* call) {
- // HACK HACK HACK
- AstPolyCallType *pct = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyCallType), Ast_Kind_Poly_Call_Type);
- pct->token = call->token;
- pct->__unused = call->next;
- pct->callee = (AstType *) call->callee;
- pct->params = (AstNode **) call->args.values;
- bh_arr_each(AstNode *, pp, pct->params) {
- if ((*pp)->kind == Ast_Kind_Argument) {
- *pp = (AstNode *) (*(AstArgument **) pp)->value;
- }
- }
-
- return pct;
-}
+++ /dev/null
-#include "astnodes.h"
-#include "types.h"
-#include "errors.h"
-#include "utils.h"
-
-AstBasicType basic_type_void = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Void] };
-AstBasicType basic_type_bool = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Bool] };
-AstBasicType basic_type_i8 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I8] };
-AstBasicType basic_type_u8 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_U8] };
-AstBasicType basic_type_i16 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I16] };
-AstBasicType basic_type_u16 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_U16] };
-AstBasicType basic_type_i32 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I32] };
-AstBasicType basic_type_u32 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_U32] };
-AstBasicType basic_type_i64 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I64] };
-AstBasicType basic_type_u64 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_U64] };
-AstBasicType basic_type_f32 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_F32] };
-AstBasicType basic_type_f64 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_F64] };
-AstBasicType basic_type_rawptr = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Rawptr] };
-AstBasicType basic_type_type_expr = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, NULL, NULL, NULL, 0, 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, NULL, 0, NULL, &basic_types[Basic_Kind_Int_Unsized] };
-AstBasicType basic_type_float_unsized = { Ast_Kind_Basic_Type, 0, NULL, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_Float_Unsized] };
-
-static OnyxToken simd_token = { Token_Type_Symbol, 0, "", { 0 } };
-AstBasicType basic_type_i8x16 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I8X16] };
-AstBasicType basic_type_i16x8 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I16X8] };
-AstBasicType basic_type_i32x4 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I32X4] };
-AstBasicType basic_type_i64x2 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_I64X2] };
-AstBasicType basic_type_f32x4 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_F32X4] };
-AstBasicType basic_type_f64x2 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, NULL, &basic_types[Basic_Kind_F64X2] };
-AstBasicType basic_type_v128 = { Ast_Kind_Basic_Type, Ast_Flag_Comptime, &simd_token, NULL, NULL, 0, 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, NULL, 0, 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 } };
-static OnyxToken builtin_tls_base_token = { Token_Type_Symbol, 10, "__tls_base ", { 0 } };
-static OnyxToken builtin_tls_size_token = { Token_Type_Symbol, 10, "__tls_size ", { 0 } };
-AstGlobal builtin_heap_start = { Ast_Kind_Global, Ast_Flag_Const, &builtin_heap_start_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
-AstGlobal builtin_stack_top = { Ast_Kind_Global, 0, &builtin_stack_top_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
-AstGlobal builtin_tls_base = { Ast_Kind_Global, 0, &builtin_tls_base_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
-AstGlobal builtin_tls_size = { Ast_Kind_Global, 0, &builtin_tls_size_token, NULL, NULL, (AstType *) &basic_type_u32, NULL };
-
-AstType *builtin_string_type;
-AstType *builtin_cstring_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;
-AstType *builtin_link_options_type;
-
-AstTyped *type_table_node = NULL;
-AstTyped *foreign_blocks_node = NULL;
-AstType *foreign_block_type = NULL;
-AstTyped *tagged_procedures_node = NULL;
-AstFunction *builtin_initialize_data_segments = NULL;
-AstFunction *builtin_run_init_procedures = NULL;
-bh_arr(AstFunction *) init_procedures = 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 },
- { "builtin", "__tls_base", (AstNode *) &builtin_tls_base },
- { "builtin", "__tls_size", (AstNode *) &builtin_tls_size },
-
- { NULL, NULL, NULL },
-};
-
-IntrinsicTable 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 },
-
- { "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 },
-
- { "__atomic_wait", ONYX_INTRINSIC_ATOMIC_WAIT },
- { "__atomic_notify", ONYX_INTRINSIC_ATOMIC_NOTIFY },
-
- { "__atomic_fence", ONYX_INTRINSIC_ATOMIC_FENCE },
-
- { "__atomic_load", ONYX_INTRINSIC_ATOMIC_LOAD },
- { "__atomic_store", ONYX_INTRINSIC_ATOMIC_STORE },
- { "__atomic_add", ONYX_INTRINSIC_ATOMIC_ADD },
- { "__atomic_sub", ONYX_INTRINSIC_ATOMIC_SUB },
- { "__atomic_and", ONYX_INTRINSIC_ATOMIC_AND },
- { "__atomic_or", ONYX_INTRINSIC_ATOMIC_OR },
- { "__atomic_xor", ONYX_INTRINSIC_ATOMIC_XOR },
- { "__atomic_xchg", ONYX_INTRINSIC_ATOMIC_XCHG },
- { "__atomic_cmpxchg", ONYX_INTRINSIC_ATOMIC_CMPXCHG },
-
- { 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, context.global_scope->created_at);
- assert(p);
-
- symbol_builtin_introduce(p->scope, bsym->sym, bsym->node);
- }
- bsym++;
- }
-
- Package* p = package_lookup_or_create("builtin", context.global_scope, a, context.global_scope->created_at);
-
- builtin_string_type = (AstType *) symbol_raw_resolve(p->scope, "str");
- if (builtin_string_type == NULL) {
- onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'str' struct not found in builtin package.");
- return;
- }
-
- builtin_cstring_type = (AstType *) symbol_raw_resolve(p->scope, "cstr");
- if (builtin_cstring_type == NULL) {
- onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'cstr' type 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 }, Error_Critical, "'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 }, Error_Critical, "'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 }, Error_Critical, "'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 }, Error_Critical, "'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 }, Error_Critical, "'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 }, Error_Critical, "'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 }, Error_Critical, "'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 }, Error_Critical, "'Code' struct not found in builtin package.");
- return;
- }
-
- builtin_initialize_data_segments = (AstFunction *) symbol_raw_resolve(p->scope, "__initialize_data_segments");
- if (builtin_initialize_data_segments == NULL || builtin_initialize_data_segments->kind != Ast_Kind_Function) {
- onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'__initialize_data_segments' procedure not found in builtin package.");
- return;
- }
-
- builtin_run_init_procedures = (AstFunction *) symbol_raw_resolve(p->scope, "__run_init_procedures");
- if (builtin_run_init_procedures == NULL || builtin_run_init_procedures->kind != Ast_Kind_Function) {
- onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'__run_init_procedures' procedure not found.");
- return;
- }
-
- builtin_link_options_type = (AstType *) symbol_raw_resolve(p->scope, "Link_Options");
- if (builtin_link_options_type == NULL) {
- onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'Link_Options' type not found.");
- return;
- }
-
- bh_arr_new(global_heap_allocator, init_procedures, 4);
-
- fori (i, 0, Binary_Op_Count) {
- bh_arr_new(global_heap_allocator, operator_overloads[i], 4);
- }
-
- IntrinsicMap* intrinsic = &builtin_intrinsics[0];
- while (intrinsic->name != NULL) {
- shput(intrinsic_table, intrinsic->name, intrinsic->intrinsic);
- intrinsic++;
- }
-}
-
-void initalize_special_globals() {
- Package *p = package_lookup("runtime.info");
- if (p != NULL) {
- type_table_node = (AstTyped *) symbol_raw_resolve(p->scope, "type_table");
- foreign_blocks_node = (AstTyped *) symbol_raw_resolve(p->scope, "foreign_blocks");
- foreign_block_type = (AstType *) symbol_raw_resolve(p->scope, "foreign_block");
- tagged_procedures_node = (AstTyped *) symbol_raw_resolve(p->scope, "tagged_procedures");
- }
-}
-
-void introduce_build_options(bh_allocator a) {
- Package* p = package_lookup_or_create("runtime", context.global_scope, a, context.global_scope->created_at);
-
- AstType* Runtime_Type = (AstType *) symbol_raw_resolve(p->scope, "Runtime");
- if (Runtime_Type == NULL) {
- onyx_report_error((OnyxFilePos) {0}, Error_Critical, "'Runtime' type not found in package runtime.");
- return;
- }
-
- AstNumLit* runtime_type = make_int_literal(a, context.options->runtime);
- runtime_type->type_node = Runtime_Type;
- add_entities_for_node(NULL, (AstNode *) runtime_type, NULL, NULL);
- symbol_builtin_introduce(p->scope, "runtime", (AstNode *) runtime_type);
-
- AstNumLit* multi_threaded = make_int_literal(a, context.options->use_multi_threading);
- multi_threaded->type_node = (AstType *) &basic_type_bool;
- symbol_builtin_introduce(p->scope, "Multi_Threading_Enabled", (AstNode *) multi_threaded);
-
- AstNumLit* wait_notify_available = make_int_literal(a, context.options->use_multi_threading && context.options->runtime == Runtime_Js);
- wait_notify_available->type_node = (AstType *) &basic_type_bool;
- symbol_builtin_introduce(p->scope, "Wait_Notify_Available", (AstNode *) wait_notify_available);
-
- i32 os;
- #ifdef _BH_LINUX
- os = 1;
- #endif
- #ifdef _BH_WINDOWS
- os = 2;
- #endif
-
- AstType* OS_Type = (AstType *) symbol_raw_resolve(p->scope, "OS");
- if (OS_Type == NULL) {
- onyx_report_error((OnyxFilePos) {0}, Error_Critical, "'OS' type not found in package runtime.");
- return;
- }
-
- AstNumLit* os_type = make_int_literal(a, os);
- os_type->type_node = OS_Type;
- add_entities_for_node(NULL, (AstNode *) os_type, NULL, NULL);
- symbol_builtin_introduce(p->scope, "compiler_os", (AstNode *) os_type);
-
- i32 arch = 0;
- #if defined(__x86_64__) || defined(_M_X64)
- arch = 1; // X86_64;
- #elif defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86)
- arch = 2; // X86_32;
- #elif defined(__aarch64__) || defined(_M_ARM64)
- arch = 3; // AARCH64;
- #endif
-
- AstType* Arch_Type = (AstType *) symbol_raw_resolve(p->scope, "Arch");
- if (Arch_Type == NULL) {
- onyx_report_error((OnyxFilePos) {0}, Error_Critical, "'Arch' type not found in package runtime.");
- return;
- }
-
- AstNumLit* arch_type = make_int_literal(a, arch);
- arch_type->type_node = Arch_Type;
- add_entities_for_node(NULL, (AstNode *) arch_type, NULL, NULL);
- symbol_builtin_introduce(p->scope, "arch", (AstNode *) arch_type);
-
- if (context.options->generate_foreign_info) {
- AstNumLit* foreign_info = make_int_literal(a, 1);
- foreign_info->type_node = (AstType *) &basic_type_bool;
- symbol_builtin_introduce(p->scope, "Generated_Foreign_Info", (AstNode *) foreign_info);
- }
-}
-
+++ /dev/null
-#define BH_DEBUG
-#include "parser.h"
-#include "utils.h"
-
-// All of the `check` functions return a boolean that signals if an issue
-// was reached while processing the node. These error booleans propagate
-// up the call stack until they reach `check_entity`.
-
-#define CHECK(kind, ...) do { \
- CheckStatus cs = check_ ## kind (__VA_ARGS__); \
- if (cs > Check_Errors_Start) return cs; \
- } while (0)
-
-#define YIELD(loc, msg) do { \
- if (context.cycle_detected) { \
- onyx_report_error(loc, Error_Waiting_On, msg); \
- return Check_Error; \
- } else { \
- return Check_Yield_Macro; \
- } \
- } while (0)
-
-#define YIELD_(loc, msg, ...) do { \
- if (context.cycle_detected) { \
- onyx_report_error(loc, Error_Waiting_On, msg, __VA_ARGS__); \
- return Check_Error; \
- } else { \
- return Check_Yield_Macro; \
- } \
- } while (0)
-
-#define YIELD_ERROR(loc, msg) do { \
- if (context.cycle_detected) { \
- onyx_report_error(loc, Error_Critical, msg); \
- return Check_Error; \
- } else { \
- return Check_Yield_Macro; \
- } \
- } while (0)
-
-#define ERROR(loc, msg) do { \
- onyx_report_error(loc, Error_Critical, msg); \
- return Check_Error; \
- } while (0)
-
-#define ERROR_(loc, msg, ...) do { \
- onyx_report_error(loc, Error_Critical, msg, __VA_ARGS__); \
- return Check_Error; \
- } while (0)
-
-#define TYPE_CHECK_(expr, type, type_name) \
- TypeMatch type_name; \
- type_name = unify_node_and_type(expr, type); \
- if (type_name == TYPE_MATCH_YIELD) YIELD((*expr)->token->pos, "Waiting on type checking."); \
- if (type_name == TYPE_MATCH_FAILED)
-
-#define CONCAT(a, b) a##_##b
-#define DEFER_LINE(a, line) CONCAT(a, line)
-#define TYPE_CHECK(expr, type) TYPE_CHECK_(expr, type, DEFER_LINE(tc, __LINE__))
-
-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_Failed, // The node is done processing and should be put in the state of Failed.
- 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** paof);
-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_temp_function_header(AstFunction* func);
-CheckStatus check_function_header(AstFunction* func);
-CheckStatus check_memres_type(AstMemRes* memres);
-CheckStatus check_memres(AstMemRes* memres);
-CheckStatus check_type(AstType** ptype);
-CheckStatus check_insert_directive(AstDirectiveInsert** pinsert);
-CheckStatus check_directive_solidify(AstDirectiveSolidify** psolid);
-CheckStatus check_do_block(AstDoBlock** pdoblock);
-CheckStatus check_constraint(AstConstraint *constraint);
-CheckStatus check_constraint_context(ConstraintContext *cc, Scope *scope, OnyxFilePos pos);
-CheckStatus check_polyquery(AstPolyQuery *query);
-
-// HACK HACK HACK
-b32 expression_types_must_be_known = 0;
-b32 all_checks_are_final = 1;
-b32 inside_for_iterator = 0;
-
-#define STATEMENT_LEVEL 1
-#define EXPRESSION_LEVEL 2
-u32 current_checking_level=0;
-
-static inline void fill_in_type(AstTyped* node) {
- if (node->type == NULL) {
- if (check_type(&node->type_node) > Check_Errors_Start) return;
-
- 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_ERROR(retnode->token->pos, "Unable to determine the automatic return type here.");
-
- *expected_return_type = retnode->expr->type;
- return Check_Success;
- }
-
- TYPE_CHECK(&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));
- }
-
- //
- // Catch the obvious case of return '^.{ ... }', as that will never
- // be legal.
- if (retnode->expr->kind == Ast_Kind_Address_Of) {
- AstAddressOf *aof = (AstAddressOf *) retnode->expr;
- if (node_is_addressable_literal((AstNode *) aof->expr)) {
- ERROR(retnode->token->pos, "Cannot return a pointer to a literal, as the space reserved for the literal will be freed upon returning.");
- }
- }
-
- } 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);
- ifnode->true_stmt->rules = Block_Rule_Macro;
- }
-
- } else {
- if (ifnode->false_stmt != NULL) {
- CHECK(statement, (AstNode **) &ifnode->false_stmt);
- ifnode->false_stmt->rules = Block_Rule_Macro;
- }
- }
-
- } 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) {
- if (whilenode->bottom_test) {
- ERROR(whilenode->token->pos, "while-loops with an 'else' clause cannot be bottom tested.");
- }
-
- CHECK(statement, (AstNode **) &whilenode->false_stmt);
- }
-
- return Check_Success;
-}
-
-CheckStatus check_for(AstFor* fornode) {
- b32 old_inside_for_iterator;
- if (fornode->flags & Ast_Flag_Has_Been_Checked) goto fornode_expr_checked;
-
- 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.");
-
- OnyxFilePos error_loc = fornode->var->token->pos;
- if (error_loc.filename == NULL) {
- error_loc = fornode->token->pos;
- }
-
- fornode->loop_type = For_Loop_Invalid;
- if (types_are_compatible(iter_type, &basic_types[Basic_Kind_I32])) {
- if (fornode->by_pointer) {
- ERROR(error_loc, "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(error_loc, "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 = type_make_pointer(context.ast_alloc, iter_type->Slice.elem);
- else fornode->var->type = iter_type->Slice.elem;
-
- fornode->loop_type = For_Loop_Slice;
-
- }
- else if (iter_type->kind == Type_Kind_VarArgs) {
- if (fornode->by_pointer) {
- ERROR_(error_loc, "Cannot iterate by pointer over '%s'.", type_get_name(iter_type));
- }
-
- fornode->var->type = iter_type->VarArgs.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 = type_make_pointer(context.ast_alloc, iter_type->DynArray.elem);
- else fornode->var->type = iter_type->DynArray.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(error_loc, "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_(error_loc, "Cannot iterate over a '%s'.", type_get_name(iter_type));
-
- if (fornode->no_close && fornode->loop_type != For_Loop_Iterator) {
- onyx_report_warning(error_loc, "Warning: #no_close here is meaningless as the iterable is not an iterator.");
- }
-
- fornode->flags |= Ast_Flag_Has_Been_Checked;
-
-fornode_expr_checked:
- old_inside_for_iterator = inside_for_iterator;
- inside_for_iterator = 0;
- iter_type = fornode->iter->type;
- if (type_struct_constructed_from_poly_struct(iter_type, builtin_iterator_type)) {
- inside_for_iterator = 1;
- }
-
- do {
- CheckStatus cs = check_block(fornode->stmt);
- inside_for_iterator = old_inside_for_iterator;
- if (cs > Check_Errors_Start) return cs;
- } while(0);
-
- return Check_Success;
-}
-
-static b32 add_case_to_switch_statement(AstSwitch* switchnode, u64 case_value, AstBlock* block, OnyxFilePos pos) {
- assert(switchnode->switch_kind == Switch_Kind_Integer);
-
- 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, Error_Critical, "Multiple cases for values '%d'.", case_value);
- return 1;
- }
-
- bh_imap_put(&switchnode->case_map, case_value, (u64) block);
- return 0;
-}
-
-static CheckStatus collect_switch_case_blocks(AstSwitch* switchnode, AstBlock* root) {
- AstNode *walker = root->body;
- while (walker != NULL) {
- switch (walker->kind) {
- case Ast_Kind_Block:
- collect_switch_case_blocks(switchnode, (AstBlock *) walker);
- break;
-
- case Ast_Kind_Switch_Case: {
- AstSwitchCase *case_node = (AstSwitchCase *) walker;
- if (case_node->is_default) {
- if (switchnode->default_case != NULL && switchnode->default_case != case_node->block) {
- ERROR(case_node->token->pos, "Multiple #default cases given");
- ERROR(switchnode->default_case->token->pos, "Multiple #default cases given");
- return Check_Error;
- }
-
- switchnode->default_case = case_node->block;
- } else {
- bh_arr_push(switchnode->cases, case_node);
- }
- break;
- }
-
- default:
- ERROR(walker->token->pos, "This statement is not allowed here.");
- }
-
- walker = walker->next;
- }
-
- return Check_Success;
-}
-
-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 (!(switchnode->flags & Ast_Flag_Has_Been_Checked)) {
- if (resolved_expr_type == NULL) YIELD(switchnode->token->pos, "Waiting for expression type to be known.");
-
- switchnode->switch_kind = Switch_Kind_Integer;
- if (!type_is_integer(switchnode->expr->type) && switchnode->expr->type->kind != Type_Kind_Enum) {
- switchnode->switch_kind = Switch_Kind_Use_Equals;
- }
-
- switch (switchnode->switch_kind) {
- case Switch_Kind_Integer:
- switchnode->min_case = 0xffffffffffffffff;
- bh_imap_init(&switchnode->case_map, global_heap_allocator, 4);
- break;
-
- case Switch_Kind_Use_Equals:
- bh_arr_new(global_heap_allocator, switchnode->case_exprs, 4);
- break;
-
- default: assert(0);
- }
- }
- switchnode->flags |= Ast_Flag_Has_Been_Checked;
-
- // Should the case block code be checked here?
- // Or should this just exist to resolve macros and expand #unquotes
- // then the cases are consumed into the array or cases, THEN the blocks
- // are actually checked?
- if (switchnode->cases == NULL) {
- CHECK(block, switchnode->case_block);
-
- bh_arr_new(global_heap_allocator, switchnode->cases, 4);
- if (collect_switch_case_blocks(switchnode, switchnode->case_block) != Check_Success) {
- return Check_Error;
- }
-
- // This is important, otherwise if this block has to return to symbol resolution.
- switchnode->case_block->statement_idx = 0;
- }
-
- fori (i, switchnode->yield_return_index, bh_arr_length(switchnode->cases)) {
- AstSwitchCase *sc = switchnode->cases[i];
- CHECK(block, sc->block);
-
- bh_arr_each(AstTyped *, value, sc->values) {
- CHECK(expression, value);
-
- if (switchnode->switch_kind == Switch_Kind_Integer && (*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;
- }
-
- TYPE_CHECK(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));
- }
-
- switch (switchnode->switch_kind) {
- case Switch_Kind_Integer: {
- b32 is_valid;
- i64 integer_value = get_expression_integer_value(*value, &is_valid);
- if (!is_valid)
- ERROR_((*value)->token->pos, "Case statement expected compile time known integer. Got '%s'.", onyx_ast_node_kind_string((*value)->kind));
-
- if (add_case_to_switch_statement(switchnode, integer_value, sc->block, sc->block->token->pos))
- return Check_Error;
-
- break;
- }
-
- case Switch_Kind_Use_Equals: {
- // Gross
- b32 found = 0;
- bh_arr_each(CaseToBlock, ctb, switchnode->case_exprs) {
- if (ctb->original_value == *value) {
- CHECK(expression, (AstTyped **) &ctb->comparison);
- found = 1;
- break;
- }
- }
- if (found) break;
-
- CaseToBlock ctb;
- ctb.block = sc->block;
- ctb.original_value = *value;
- ctb.comparison = make_binary_op(context.ast_alloc, Binary_Op_Equal, switchnode->expr, *value);
- ctb.comparison->token = (*value)->token;
- bh_arr_push(switchnode->case_exprs, ctb);
-
- CHECK(binaryop, &bh_arr_last(switchnode->case_exprs).comparison);
- break;
- }
- }
- }
-
- switchnode->yield_return_index += 1;
- }
-
- 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 CheckStatus check_resolve_callee(AstCall* call, AstTyped** effective_callee) {
- if (call->kind == Ast_Kind_Intrinsic_Call) return Check_Success;
-
- AstTyped* callee = (AstTyped *) strip_aliases((AstNode *) call->callee);
- b32 calling_a_macro = 0;
-
- if (callee->kind == Ast_Kind_Overloaded_Function) {
- AstTyped* new_callee = find_matching_overload_by_arguments(
- ((AstOverloadedFunction *) callee)->overloads,
- &call->args);
-
- if (new_callee == NULL) {
- report_unable_to_match_overload(call, ((AstOverloadedFunction *) callee)->overloads);
- return Check_Error;
- }
-
- if (new_callee == (AstTyped *) &node_that_signals_a_yield) {
- 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, 1);
- 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 while (callee->kind == Ast_Kind_Polymorphic_Proc) {
- AstTyped* new_callee = (AstTyped *) polymorphic_proc_lookup((AstFunction *) 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;
- }
-
- // 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 (!calling_a_macro) call->callee = 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;
-}
-
-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->kind == Ast_Kind_Call) {
- AstNode* callee = strip_aliases((AstNode *) call->callee);
- if (callee->kind == Ast_Kind_Poly_Struct_Type) {
- *pcall = (AstCall *) convert_call_to_polycall(call);
- CHECK(expression, (AstTyped **) pcall);
- return Check_Success;
- }
- }
-
- 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;
-
- AstFunction* callee=NULL;
- CHECK(resolve_callee, call, (AstTyped **) &callee);
-
- if (callee->kind == Ast_Kind_Function) {
- if (callee->constraints.constraints != NULL && callee->constraints.constraints_met == 0) {
- YIELD(call->token->pos, "Waiting for constraints to be checked on callee.");
- }
- }
-
- i32 arg_count = get_argument_buffer_size(&callee->type->Function, &call->args);
- arguments_ensure_length(&call->args, arg_count);
-
- char* err_msg = NULL;
- fill_in_arguments(&call->args, (AstNode *) callee, &err_msg, 0);
- 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->data_id = 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->kind == Ast_Kind_Function && callee->is_intrinsic) {
- call->kind = Ast_Kind_Intrinsic_Call;
- call->callee = NULL;
-
- token_toggle_end(callee->intrinsic_name);
- char* intr_name = callee->intrinsic_name->text;
-
- i32 index;
- if ((index = shgeti(intrinsic_table, intr_name)) == -1) {
- onyx_report_error(callee->token->pos, Error_Critical, "Intrinsic not supported, '%s'.", intr_name);
- token_toggle_end(callee->intrinsic_name);
- return Check_Error;
- }
-
- call->intrinsic = intrinsic_table[index].value;
-
- 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.");
- }
-
- OnyxError error;
- TypeMatch tm = check_arguments_against_type(&call->args, &callee->type->Function, &call->va_kind,
- call->token, get_function_name(callee), &error);
- if (tm == TYPE_MATCH_FAILED) {
- onyx_submit_error(error);
- return Check_Error;
- }
-
- if (tm == TYPE_MATCH_YIELD) YIELD(call->token->pos, "Waiting on argument type checking.");
-
- call->flags |= Ast_Flag_Has_Been_Checked;
- 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, Error_Critical, "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, AstTyped* third_argument) {
- if (bh_arr_length(operator_overloads[binop->operation]) == 0) return NULL;
-
- if (binop->overload_args == NULL) {
- binop->overload_args = bh_alloc_item(context.ast_alloc, Arguments);
- bh_arr_new(context.ast_alloc, binop->overload_args->values, 3);
- bh_arr_set_length(binop->overload_args->values, third_argument ? 3 : 2);
-
- if (binop_is_assignment(binop->operation)) {
- binop->overload_args->values[0] = (AstTyped *) make_address_of(context.ast_alloc, binop->left);
-
- u32 current_all_checks_are_final = all_checks_are_final;
- all_checks_are_final = 0;
- u32 current_checking_level_store = current_checking_level;
- CheckStatus cs = check_address_of((AstAddressOf **) &binop->overload_args->values[0]);
- current_checking_level = current_checking_level_store;
- all_checks_are_final = current_all_checks_are_final;
-
- if (cs == Check_Yield_Macro) return (AstCall *) &node_that_signals_a_yield;
- if (cs == Check_Error) return NULL;
-
- binop->overload_args->values[0] = (AstTyped *) make_argument(context.ast_alloc, binop->overload_args->values[0]);
-
- } else {
- binop->overload_args->values[0] = (AstTyped *) make_argument(context.ast_alloc, binop->left);
- }
-
-
- binop->overload_args->values[1] = (AstTyped *) make_argument(context.ast_alloc, binop->right);
- if (third_argument != NULL) binop->overload_args->values[2] = (AstTyped *) make_argument(context.ast_alloc, third_argument);
- }
-
- AstTyped* overload = find_matching_overload_by_arguments(operator_overloads[binop->operation], binop->overload_args);
- if (overload == NULL || overload == (AstTyped *) &node_that_signals_a_yield) return (AstCall *) overload;
-
- 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;
-
- arguments_clone(&implicit_call->args, binop->overload_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) {
- if (binop->left->type_node != NULL && binop->left->entity && binop->left->entity->state <= Entity_State_Check_Types) {
- YIELD(binop->token->pos, "Waiting for type to be constructed on left hand side.");
- }
-
- // NOTE: There is a subtlety here. You cannot use the result of `resolve_expression_type` directly,
- // as in some cases (especially with macros and polyprocs), the result is not "correct". The result
- // makes them appears as though they are runtime-known values, which they are not. Using the following
- // pattern does prevent this issue.
- resolve_expression_type(binop->right);
-
- Type* right_type = get_expression_type(binop->right);
- if (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 (right_type->kind == Type_Kind_Compound) {
- AstCompound* lhs = (AstCompound *) binop->left;
- i32 expr_count = right_type->Compound.count;
- if (lhs->kind != Ast_Kind_Compound || bh_arr_length(lhs->exprs) != expr_count) {
- ERROR_(binop->token->pos, "Expected left hand side to have %d expressions.", expr_count);
- }
-
- fori (i, 0, expr_count) lhs->exprs[i]->type = right_type->Compound.types[i];
-
- lhs->type = type_build_compound_type(context.ast_alloc, lhs);
-
- } else {
- binop->left->type = right_type;
- }
- }
-
- } else {
- // NOTE: +=, -=, ...
- // NOTE: At this point, it is assumed that operator overloads for +=, -=, etc have been tested.
-
- 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.");
- }
- }
-
- TYPE_CHECK(&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 == NULL) YIELD(binop->token->pos, "Waiting for left-type to be known.");
- if (rtype == NULL) YIELD(binop->token->pos, "Waiting for right-type to be known.");
-
- 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.");
-
- TYPE_CHECK(&binop->left, rtype) {
- TYPE_CHECK(&binop->right, ltype) {
- ERROR_(binop->token->pos,
- "Cannot compare '%s' to '%s'.",
- type_get_name(binop->left->type),
- type_get_name(binop->right->type));
- }
- }
- }
-
- 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;
-
- if (binop->operation == Binary_Op_Assign && binop->left->kind == Ast_Kind_Subscript && bh_arr_length(operator_overloads[Binary_Op_Subscript_Equals]) > 0) {
- AstSubscript* sub = (AstSubscript *) binop->left;
-
- if (binop->potential_substitute == NULL) {
- u32 current_checking_level_store = current_checking_level;
- CHECK(expression, &sub->addr);
- CHECK(expression, &sub->expr);
- CHECK(expression, &binop->right);
- current_checking_level = current_checking_level_store;
-
- AstBinaryOp *op = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
- op->token = binop->token;
- op->operation = Binary_Op_Subscript_Equals;
- op->left = ((AstSubscript *) binop->left)->addr;
- op->right = ((AstSubscript *) binop->left)->expr;
-
- binop->potential_substitute = op;
- }
-
- AstCall* call = binaryop_try_operator_overload(binop->potential_substitute, binop->right);
- if (call == (AstCall *) &node_that_signals_a_yield) YIELD(binop->token->pos, "Waiting on potential operator overload.");
- if (call != NULL) {
- call->next = binop->next;
- *(AstCall **) pbinop = call;
-
- CHECK(call, (AstCall **) pbinop);
- 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;
-
- // :UnaryFieldAccessIsGross
- if (binop->left->kind == Ast_Kind_Unary_Field_Access || binop->right->kind == Ast_Kind_Unary_Field_Access) {
- TYPE_CHECK(&binop->left, binop->right->type) {
- TYPE_CHECK(&binop->right, binop->left->type) {
- // TODO: This should report a better error about the Unary_Field_Access not be able to be resolved given whatever type.
- // - brendanfh 2021/12/31
- report_bad_binaryop(binop);
- return Check_Error;
- }
- }
- }
-
- 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.");
- }
- }
-
- // NOTE: Try operator overloading before checking everything else.
- if ((binop->left->type != NULL && (binop->left->type->kind != Type_Kind_Basic || (binop->left->type->Basic.flags & Basic_Flag_SIMD) != 0))
- || (binop->right->type != NULL && (binop->right->type->kind != Type_Kind_Basic || (binop->right->type->Basic.flags & Basic_Flag_SIMD) != 0))) {
- AstCall *implicit_call = binaryop_try_operator_overload(binop, NULL);
-
- 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);
-
- if (binop->left->type == NULL && binop->left->entity && binop->left->entity->state <= Entity_State_Check_Types) {
- YIELD(binop->left->token->pos, "Waiting for this type to be known");
- }
- if (binop->right->type == NULL && binop->right->entity && binop->right->entity->state <= Entity_State_Check_Types) {
- YIELD(binop->right->token->pos, "Waiting for this type to be known");
- }
-
- // 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.");
- }
-
- TYPE_CHECK(&binop->left, binop->right->type) {
- TYPE_CHECK(&binop->right, binop->left->type) {
- 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];
- }
-
- if (all_checks_are_final) {
- 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) {
- CHECK(arguments, &sl->args);
-
- return Check_Success;
- }
-
- CHECK(expression, &sl->stnode);
- if (!node_is_type((AstNode *) sl->stnode)) {
- ERROR(sl->token->pos, "Type used for struct literal is not a type.");
- }
-
- sl->type = type_build_from_ast(context.ast_alloc, (AstType *) sl->stnode);
- if (sl->type == NULL)
- YIELD(sl->token->pos, "Trying to resolve type of struct literal.");
- }
-
- if (!type_is_structlike_strict(sl->type)) {
- //
- // If there are no given arguments to a structure literal, it is treated as a 'zero-value',
- // and can be used to create a completely zeroed value of any type.
- if (bh_arr_length(sl->args.values) == 0 && bh_arr_length(sl->args.named_values) == 0) {
- AstZeroValue *zv = make_zero_value(context.ast_alloc, sl->token, sl->type);
- bh_arr_push(sl->args.values, (AstTyped *) zv);
-
- sl->flags |= Ast_Flag_Has_Been_Checked;
- return Check_Success;
- }
-
- if ((sl->flags & Ast_Flag_Has_Been_Checked) != 0) {
- assert(sl->args.values);
- assert(sl->args.values[0]);
- assert(sl->args.values[0]->kind == Ast_Kind_Zero_Value);
- return Check_Success;
- }
-
- //
- // Otherwise, it is not possible to construct the type if it is not a structure.
- 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, 1)) {
- onyx_report_error(sl->token->pos, Error_Critical, 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, Error_Critical,
- "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);
- 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.");
- }
-
- TYPE_CHECK(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) return Check_Success;
- // YIELD(al->token->pos, "Waiting for array literal type to be known.");
-
- CHECK(expression, &al->atnode);
- if (!node_is_type((AstNode *) al->atnode))
- ERROR(al->token->pos, "Array type is not a type.");
-
- al->type = type_build_from_ast(context.ast_alloc, (AstType *) al->atnode);
- 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;
- assert(al->type->kind == Type_Kind_Array);
-
- 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);
-
- TYPE_CHECK(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;
- if (range->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
-
- CHECK(expression, &range->low);
- CHECK(expression, &range->high);
-
- builtin_range_type_type = type_build_from_ast(context.ast_alloc, builtin_range_type);
- if (builtin_range_type_type == NULL) YIELD(range->token->pos, "Waiting for 'range' structure to be built.");
-
- Type* expected_range_type = builtin_range_type_type;
- StructMember smem;
-
- type_lookup_member(expected_range_type, "low", &smem);
- TYPE_CHECK(&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);
- TYPE_CHECK(&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;
- }
-
- range->flags |= Ast_Flag_Has_Been_Checked;
- 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);
-
- TYPE_CHECK(&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) 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** paof) {
- AstAddressOf* aof = *paof;
-
- AstTyped* expr = (AstTyped *) strip_aliases((AstNode *) aof->expr);
- if (expr->kind == Ast_Kind_Subscript && bh_arr_length(operator_overloads[Binary_Op_Ptr_Subscript]) > 0) {
- if (aof->potential_substitute == NULL) {
- CHECK(expression, &((AstSubscript *) expr)->addr);
- CHECK(expression, &((AstSubscript *) expr)->expr);
-
- AstBinaryOp *op = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
- op->operation = Binary_Op_Ptr_Subscript;
- op->left = ((AstSubscript *) expr)->addr;
- op->right = ((AstSubscript *) expr)->expr;
- op->token = aof->token;
-
- aof->potential_substitute = op;
- }
-
- AstCall* call = binaryop_try_operator_overload(aof->potential_substitute, NULL);
- if (call == (AstCall *) &node_that_signals_a_yield) YIELD(aof->token->pos, "Waiting for operator overload to possibly resolve.");
- if (call != NULL) {
- call->next = aof->next;
- *(AstCall **) paof = call;
-
- CHECK(call, (AstCall **) paof);
- return Check_Success;
- }
- }
-
- CHECK(expression, &aof->expr);
- if (node_is_addressable_literal((AstNode *) aof->expr)) {
- resolve_expression_type(aof->expr);
- }
-
- if (aof->expr->type == NULL) {
- YIELD(aof->token->pos, "Trying to resolve type of expression to take a reference.");
- }
-
- expr = (AstTyped *) strip_aliases((AstNode *) aof->expr);
- if (node_is_type((AstNode *) expr)) {
- AstPointerType *pt = onyx_ast_node_new(context.ast_alloc, sizeof(AstPointerType), Ast_Kind_Pointer_Type);
- pt->token = aof->token;
- pt->elem = (AstType *) expr;
- pt->__unused = aof->next;
- *paof = (AstAddressOf *) pt;
- CHECK(type, (AstType **) &pt);
- return Check_Success;
- }
-
- if ((expr->kind != Ast_Kind_Subscript
- && expr->kind != Ast_Kind_Dereference
- && expr->kind != Ast_Kind_Field_Access
- && expr->kind != Ast_Kind_Memres
- && expr->kind != Ast_Kind_Local
- && expr->kind != Ast_Kind_Constraint_Sentinel
- && !node_is_addressable_literal((AstNode *) expr))
- || (expr->flags & Ast_Flag_Cannot_Take_Addr) != 0) {
-
- if (aof->can_be_removed) {
- *(AstTyped **) paof = aof->expr;
- return Check_Yield_Macro;
- }
-
- ERROR_(aof->token->pos, "Cannot take the address of something that is not an l-value. %s", onyx_ast_node_kind_string(expr->kind));
- }
-
- expr->flags |= Ast_Flag_Address_Taken;
-
- aof->type = type_make_pointer(context.ast_alloc, 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);
-
- if (sub->addr->type == NULL) YIELD(sub->token->pos, "Waiting to know type of left-hand side of subscript.");
-
- // NOTE: Try operator overloading before checking everything else.
- if (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, NULL);
-
- 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 (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;
- }
-
- if (types_are_compatible(sub->expr->type, builtin_range_type_type)) {
- Type *of = type_get_contained_type(sub->addr->type);
- if (of == NULL) {
- // 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 (!type_is_small_integer(sub->expr->type)) {
- report_bad_binaryop((AstBinaryOp *) sub);
- ERROR_(sub->token->pos, "Expected small integer type for index, got '%s'.", node_get_type_name(sub->expr));
- }
-
- sub->type = type_get_contained_type(sub->addr->type);
- if (sub->type == NULL) {
- 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;
- if (field->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
-
- CHECK(expression, &field->expr);
- if (field->expr->type == NULL) {
- 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;
- }
-
- if (field->token != NULL && field->field == NULL) {
- token_toggle_end(field->token);
- field->field = bh_strdup(context.ast_alloc, field->token->text);
- token_toggle_end(field->token);
- }
-
- if (field->expr->type->kind == Type_Kind_Struct) {
- if (field->expr->type->Struct.status != SPS_Uses_Done) {
- YIELD(field->token->pos, "Waiting for struct type to be completed before looking up members.");
- }
- }
-
- StructMember smem;
- if (!type_lookup_member(field->expr->type, field->field, &smem)) {
- if (field->expr->type->kind == Type_Kind_Array) {
- if (!strcmp(field->field, "count")) {
- *pfield = (AstFieldAccess *) make_int_literal(context.ast_alloc, field->expr->type->Array.count);
- return Check_Success;
- }
- }
-
- AstNode* n = try_symbol_raw_resolve_from_type(field->expr->type, field->field);
-
- AstType* type_node = field->expr->type->ast_type;
- if (!n) n = try_symbol_raw_resolve_from_node((AstNode *) type_node, field->field);
-
- if (n) {
- *pfield = (AstFieldAccess *) n;
- return Check_Success;
- }
-
- char* closest = find_closest_symbol_in_node((AstNode *) type_node, field->field);
- if (closest) {
- ERROR_(field->token->pos, "Field '%s' does not exists on '%s'. Did you mean '%s'?", field->field, node_get_type_name(field->expr), closest);
- } else {
- ERROR_(field->token->pos, "Field '%s' does not exists on '%s'.", field->field, node_get_type_name(field->expr));
- }
- }
-
- // NOTE: If this member was included into the structure through a "use x: ^T" kind of statement,
- // then we have to insert a intermediate field access in order to access the correct member.
- if (smem.use_through_pointer_index >= 0) {
- StructMember containing_member;
- assert(type_lookup_member_by_idx(field->expr->type, smem.use_through_pointer_index, &containing_member));
-
- AstFieldAccess *new_access = onyx_ast_node_new(context.ast_alloc, sizeof(AstFieldAccess), Ast_Kind_Field_Access);
- new_access->token = field->token;
- new_access->offset = containing_member.offset;
- new_access->idx = containing_member.idx;
- new_access->type = containing_member.type;
- new_access->expr = field->expr;
- new_access->flags |= Ast_Flag_Has_Been_Checked;
-
- field->expr = (AstTyped *) new_access;
- }
-
- field->offset = smem.offset;
- field->idx = smem.idx;
- field->type = smem.type;
- field->flags |= Ast_Flag_Has_Been_Checked;
- return Check_Success;
-}
-
-CheckStatus check_method_call(AstBinaryOp** pmcall) {
- AstBinaryOp* mcall = *pmcall;
- CHECK(expression, &mcall->left);
- if (mcall->left->type == NULL) YIELD(mcall->token->pos, "Trying to resolve type of left hand side.");
-
- mcall->type = mcall->left->type;
- 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) {
- AstAddressOf *address_of = make_address_of(context.ast_alloc, implicit_argument);
- address_of->can_be_removed = 1;
- implicit_argument = (AstTyped *) address_of;
- }
-
- 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;
-
- *pmcall = (AstBinaryOp *) mcall->right;
- mcall->right->next = mcall->next;
- }
- mcall->flags |= Ast_Flag_Has_Been_Checked;
-
- CHECK(call, (AstCall **) pmcall);
- return Check_Success;
-}
-
-CheckStatus check_size_of(AstSizeOf* so) {
- 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);
- so->flags |= Ast_Flag_Comptime;
-
- return Check_Success;
-}
-
-CheckStatus check_align_of(AstAlignOf* ao) {
- 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);
- ao->flags |= Ast_Flag_Comptime;
-
- 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.
- CHECK(type, (AstType **) pexpr);
- expr = *pexpr;
-
- // 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.");
- }
- } else {
- type_build_from_ast(context.ast_alloc, (AstType*) expr);
- }
-
- 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;
- }
-
- if (expr->kind == Ast_Kind_Directive_Init) {
- ERROR(expr->token->pos, "#init declarations are not in normal expressions, only in #after clauses.");
- }
-
- 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_Intrinsic_Call:
- 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:
- YIELD_(expr->token->pos, "Waiting to resolve symbol, '%b'.", expr->token->text, expr->token->length);
- break;
-
- case Ast_Kind_Param:
- if (expr->type == NULL) {
- YIELD(expr->token->pos, "Waiting on parameter type.");
- }
- break;
-
- case Ast_Kind_Local: break;
-
- case Ast_Kind_Address_Of: retval = check_address_of((AstAddressOf **) pexpr); 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, Error_Critical, "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:
- CHECK(directive_solidify, (AstDirectiveSolidify **) pexpr);
- 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_Memres:
- if (expr->type == NULL) YIELD(expr->token->pos, "Waiting to know globals type.");
- 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_Polymorphic_Proc: break;
- case Ast_Kind_Package: break;
- case Ast_Kind_Error: break;
- case Ast_Kind_Unary_Field_Access: break;
- case Ast_Kind_Constraint_Sentinel: break;
- case Ast_Kind_Switch_Case: break;
- case Ast_Kind_Foreign_Block: break;
- case Ast_Kind_Zero_Value: break;
-
- default:
- retval = Check_Error;
- onyx_report_error(expr->token->pos, Error_Critical, "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);
-
- TYPE_CHECK(&insert->code_expr, code_type) {
- ERROR_(insert->token->pos, "#unquote 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_directive_solidify(AstDirectiveSolidify** psolid) {
- AstDirectiveSolidify* solid = *psolid;
-
- bh_arr_each(AstPolySolution, sln, solid->known_polyvars) {
- CHECK(expression, &sln->value);
-
- 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;
- }
- }
-
- solid->resolved_proc = polymorphic_proc_try_solidify(solid->poly_proc, solid->known_polyvars, solid->token);
- if (solid->resolved_proc == (AstNode *) &node_that_signals_a_yield) {
- solid->resolved_proc = NULL;
- YIELD(solid->token->pos, "Waiting for partially solidified procedure.");
- }
-
- // NOTE: Not a DirectiveSolidify.
- *psolid = (AstDirectiveSolidify *) solid->resolved_proc;
-
- return Check_Success;
-}
-
-CheckStatus check_remove_directive(AstDirectiveRemove *remove) {
- if (!inside_for_iterator) {
- ERROR(remove->token->pos, "#remove is only allowed in the body of a for-loop over an iterator.");
- }
-
- return Check_Success;
-}
-
-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_Directive_Remove: return check_remove_directive((AstDirectiveRemove *) stmt);
- case Ast_Kind_Call: {
- CHECK(call, (AstCall **) pstmt);
- (*pstmt)->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.");
- }
-
- if (typed_stmt->next != NULL && typed_stmt->next->kind == Ast_Kind_Binary_Op) {
- AstBinaryOp *next = (AstBinaryOp *) typed_stmt->next;
- if (next->operation == Binary_Op_Assign && next->left == typed_stmt) {
- typed_stmt->flags |= Ast_Flag_Decl_Followed_By_Init;
- }
- }
- 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) {
- // This used to use statement_chain, but since block optimize which statements need to be rechecked,
- // it has to be its own thing.
-
- AstNode** start = &block->body;
- fori (i, 0, block->statement_idx) {
- start = &(*start)->next;
- }
-
- while (*start) {
- CheckStatus cs = check_statement(start);
- switch (cs) {
- case Check_Success:
- start = &(*start)->next;
- block->statement_idx++;
- break;
-
- case Check_Return_To_Symres:
- block->statement_idx = 0;
-
- default:
- return cs;
- }
-
- }
-
- return Check_Success;
-}
-
-CheckStatus check_function(AstFunction* func) {
- if (func->flags & Ast_Flag_Has_Been_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");
-
- bh_arr_each(AstTyped *, pexpr, func->tags) {
- CHECK(expression, pexpr);
-
- if (((*pexpr)->flags & Ast_Flag_Comptime) == 0) {
- ERROR((*pexpr)->token->pos, "#tag expressions should be compile time known.");
- }
- }
-
- inside_for_iterator = 0;
- 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) {
- expected_return_type = NULL;
- return status;
- }
- }
-
- if (*expected_return_type == &type_auto_return) {
- *expected_return_type = &basic_types[Basic_Kind_Void];
- }
-
- func->flags |= Ast_Flag_Has_Been_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, Error_Critical, "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.");
-
- if (s_node->min_size_) CHECK(expression, &s_node->min_size_);
- if (s_node->min_alignment_) CHECK(expression, &s_node->min_alignment_);
-
- if (s_node->polymorphic_argument_types) {
- assert(s_node->polymorphic_arguments);
-
- fori (i, 0, (i64) bh_arr_length(s_node->polymorphic_argument_types)) {
- Type *arg_type = type_build_from_ast(context.ast_alloc, s_node->polymorphic_argument_types[i]);
- if (arg_type == NULL) YIELD(s_node->polymorphic_argument_types[i]->token->pos, "Waiting to build type for polymorph argument.");
-
- // CLEANUP: This might be wrong...
- if (s_node->polymorphic_arguments[i].value) {
- TYPE_CHECK(&s_node->polymorphic_arguments[i].value, arg_type) {
- ERROR_(s_node->polymorphic_arguments[i].value->token->pos, "Expected value of type %s, got %s.",
- type_get_name(arg_type),
- type_get_name(s_node->polymorphic_arguments[i].value->type));
- }
- }
- }
- }
-
- if (s_node->constraints.constraints) {
- s_node->constraints.produce_errors = (s_node->flags & Ast_Flag_Header_Check_No_Error) == 0;
-
- OnyxFilePos pos = s_node->token->pos;
- if (s_node->polymorphic_error_loc.filename) {
- pos = s_node->polymorphic_error_loc;
- }
- CHECK(constraint_context, &s_node->constraints, s_node->scope, pos);
- }
-
- //
- // This ensures that all procedures defined inside of a structure are
- // not pruned and omitted from the binary. This is because a large
- // use-case of procedures in structures is dynamically linking them
- // using the type info data.
- if (s_node->scope) {
- fori (i, 0, shlen(s_node->scope->symbols)) {
- AstNode* node = s_node->scope->symbols[i].value;
- if (node->kind == Ast_Kind_Function) {
- node->flags |= Ast_Flag_Function_Used;
- }
- }
- }
-
- 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 pending_type.
- s_node->ready_to_build_type = 1;
- type_build_from_ast(context.ast_alloc, (AstType *) s_node);
- if (s_node->pending_type == NULL || !s_node->pending_type_is_valid)
- YIELD(s_node->token->pos, "Waiting for type to be constructed.");
-
- bh_arr_each(StructMember *, smem, s_node->pending_type->Struct.memarr) {
- if ((*smem)->type->kind == Type_Kind_Compound) {
- ERROR(s_node->token->pos, "Compound types are not allowed as struct member types.");
- }
-
- if ((*smem)->used) {
- if (!type_struct_member_apply_use(context.ast_alloc, s_node->pending_type, *smem)) {
- YIELD((*smem)->token->pos, "Waiting for use to be applied.");
- }
- }
- }
-
- s_node->stcache = s_node->pending_type;
- s_node->stcache->Struct.status = SPS_Uses_Done;
-
- 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.");
- if (s_node->entity_type && s_node->entity_type->state == Entity_State_Failed)
- return Check_Failed;
-
- if (s_node->meta_tags) {
- bh_arr_each(AstTyped *, meta, s_node->meta_tags) {
- CHECK(expression, meta);
- resolve_expression_type(*meta);
-
- if (((*meta)->flags & Ast_Flag_Comptime) == 0) {
- onyx_report_error((*meta)->token->pos, Error_Critical, "#tag expressions are expected to be compile-time known.");
- return Check_Error;
- }
- }
- }
-
- bh_arr_each(StructMember *, smem, s_node->stcache->Struct.memarr) {
- if ((*smem)->initial_value && *(*smem)->initial_value) {
- CHECK(expression, (*smem)->initial_value);
-
- TYPE_CHECK((*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);
- }
-
- if ((*smem)->meta_tags) {
- bh_arr_each(AstTyped *, meta, (*smem)->meta_tags) {
- CHECK(expression, meta);
- resolve_expression_type(*meta);
-
- if (((*meta)->flags & Ast_Flag_Comptime) == 0) {
- onyx_report_error((*meta)->token->pos, Error_Critical, "#tag expressions are expected to be compile-time known.");
- return Check_Error;
- }
- }
- }
- }
-
- return Check_Success;
-}
-
-CheckStatus check_temp_function_header(AstFunction* func) {
- CheckStatus cs = check_function_header(func);
- if (cs == Check_Error) {
- if (func->flags & Ast_Flag_Header_Check_No_Error) {
- onyx_clear_errors();
- }
-
- return Check_Failed;
- }
-
- if (cs != Check_Success) return cs;
-
- return Check_Complete;
-}
-
-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;
-
- if (func->constraints.constraints != NULL && func->constraints.constraints_met == 0) {
- func->constraints.produce_errors = (func->flags & Ast_Flag_Header_Check_No_Error) == 0;
- CHECK(constraint_context, &func->constraints, func->scope, func->token->pos);
-
- // All constraints have been met. Return to symbol resolution to finish
- // looking up all symbols in the function.
- return Check_Return_To_Symres;
- }
-
- 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) {
- // If the function has the no_error flag, then the type node should have it set too.
- // This allows for polymorphic structures with constraints to fail gracefully.
- local->type_node->flags |= (func->flags & Ast_Flag_Header_Check_No_Error);
- CHECK(type, &local->type_node);
- }
-
- fill_in_type((AstTyped *) local);
- if (local->type == NULL) {
- YIELD(local->token->pos, "Waiting for parameter type to be known.");
- }
-
- if (local->type == (Type *) &node_that_signals_failure) {
- return Check_Failed;
- }
-
- 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 (!unify_node_and_type(¶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) {
- assert(memres->type_entity);
- if (memres->type_entity->state < Entity_State_Code_Gen) YIELD(memres->token->pos, "Waiting for global to pass type construction.");
-
- if (memres->initial_value != NULL) {
- if (memres->threadlocal) {
- onyx_report_error(memres->token->pos, Error_Critical, "'#thread_local' variables cannot have an initializer at the moment.");
- return Check_Error;
- }
-
- CHECK(expression, &memres->initial_value);
-
- if (memres->type != NULL) {
- Type* memres_type = memres->type;
- TYPE_CHECK(&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 {
- resolve_expression_type(memres->initial_value);
- 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;
- }
-
- if ((memres->initial_value->flags & Ast_Flag_Comptime) == 0) {
- if (memres->initial_value->entity != NULL && memres->initial_value->entity->state <= Entity_State_Check_Types) {
- YIELD(memres->token->pos, "Waiting for initial value to be checked.");
- }
-
- ERROR(memres->initial_value->token->pos, "Top level expressions must be compile time known.");
- }
- }
-
- return Check_Success;
-}
-
-CheckStatus check_type(AstType** ptype) {
- if (ptype == NULL || *ptype == NULL) return Check_Success;
-
- AstType* type = *ptype;
- AstType* original_type = type;
- while (type->kind == Ast_Kind_Type_Alias)
- type = ((AstTypeAlias *) type)->to;
-
- if (type->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
-
- 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);
- fill_in_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: ((AstPointerType *) type)->elem->flags |= type->flags & Ast_Flag_Header_Check_No_Error; CHECK(type, &((AstPointerType *) type)->elem); break;
- case Ast_Kind_Slice_Type: ((AstSliceType *) type)->elem->flags |= type->flags & Ast_Flag_Header_Check_No_Error; CHECK(type, &((AstSliceType *) type)->elem); break;
- case Ast_Kind_DynArr_Type: ((AstDynArrType *) type)->elem->flags |= type->flags & Ast_Flag_Header_Check_No_Error; CHECK(type, &((AstDynArrType *) type)->elem); break;
- case Ast_Kind_VarArg_Type: ((AstVarArgType *) type)->elem->flags |= type->flags & Ast_Flag_Header_Check_No_Error; 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;
- }
-
- case Ast_Kind_Array_Type: {
- AstArrayType* atype = (AstArrayType *) type;
- if (atype->count_expr) {
- CHECK(expression, &atype->count_expr);
- resolve_expression_type(atype->count_expr);
- }
-
- break;
- }
-
- case Ast_Kind_Field_Access: {
- CHECK(field_access, (AstFieldAccess **) ptype);
- type = *ptype;
- original_type = type;
-
- if (!node_is_type((AstNode *) type)) {
- ERROR_(original_type->token->pos, "This field access did not resolve to be a type. It resolved to be a '%s'.", onyx_ast_node_kind_string(type->kind));
- }
- break;
- }
- }
-
- type = original_type;
- type->flags |= Ast_Flag_Comptime;
- while (type->kind == Ast_Kind_Type_Alias) {
- type->flags |= Ast_Flag_Comptime;
- type = ((AstTypeAlias *) type)->to;
- }
-
- type->flags |= Ast_Flag_Has_Been_Checked;
- return Check_Success;
-}
-
-CheckStatus check_static_if(AstIf* static_if) {
- expression_types_must_be_known = 1;
- CheckStatus result = check_expression(&static_if->cond);
- expression_types_must_be_known = 0;
- if (result == Check_Yield_Macro) return Check_Yield_Macro;
-
- 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) {
- AstDirectiveExport *export = (AstDirectiveExport *) directive;
- AstTyped *exported = export->export;
- if (exported->entity && exported->entity->state <= Entity_State_Check_Types)
- YIELD(directive->token->pos, "Waiting for exported type to be known.");
-
- CHECK(expression, &export->export_name_expr);
-
- if (export->export_name_expr->kind != Ast_Kind_StrLit) {
- ERROR_(export->token->pos, "Expected export name to be a string literal, got '%s'.", onyx_ast_node_kind_string(export->export_name_expr->kind));
- }
-
- export->export_name = export->export_name_expr->token;
- }
-
- if (directive->kind == Ast_Kind_Directive_Init) {
- AstDirectiveInit *init = (AstDirectiveInit *) directive;
- if ((init->flags & Ast_Flag_Has_Been_Checked) == 0) {
- CHECK(expression, &init->init_proc);
-
- if (init->init_proc->kind != Ast_Kind_Function) {
- ERROR_(init->token->pos, "#init only works for functions, got '%s'", onyx_ast_node_kind_string(init->init_proc->kind));
- }
-
- assert(init->init_proc->type);
- if (init->init_proc->type->Function.param_count != 0) {
- ERROR(init->token->pos, "#init expects a function that takes 0 arguments.");
- }
- }
-
- init->flags |= Ast_Flag_Has_Been_Checked;
-
- if (init->dependencies) {
- i32 i = 0;
- bh_arr_each(AstDirectiveInit *, dependency, init->dependencies) {
- AstTyped *d = (AstTyped *) strip_aliases((AstNode *) *dependency);
- if (d->kind != Ast_Kind_Directive_Init) {
- ERROR_(init->token->pos, "All dependencies of an #init must be another #init. The %d%s dependency was not.", i + 1, bh_num_suffix(i + 1));
- }
-
- assert(d->entity);
- if (d->entity->state != Entity_State_Finalized) {
- YIELD(init->token->pos, "Circular dependency in #init nodes. Here are the nodes involved.");
- }
-
- i++;
- }
- }
-
- bh_arr_push(init_procedures, (AstFunction *) init->init_proc);
- return Check_Complete;
- }
-
- if (directive->kind == Ast_Kind_Directive_Library) {
- AstDirectiveLibrary *library = (AstDirectiveLibrary *) directive;
-
- if (library->library_symbol->kind != Ast_Kind_StrLit) {
- ERROR_(library->token->pos, "#library directive expected compile-time known string for library name. Got '%s'.",
- onyx_ast_node_kind_string(library->library_symbol->kind));
- }
-
- AstStrLit *symbol = (AstStrLit *) library->library_symbol;
- char* temp_name = bh_alloc_array(global_scratch_allocator, char, symbol->token->length);
- i32 temp_name_len = string_process_escape_seqs(temp_name, symbol->token->text, symbol->token->length);
- library->library_name = bh_strdup(global_heap_allocator, temp_name);
- return Check_Success;
- }
-
- 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_constraint(AstConstraint *constraint) {
- switch (constraint->phase) {
- case Constraint_Phase_Cloning_Expressions: {
- if (constraint->interface->kind == Ast_Kind_Symbol) {
- return Check_Return_To_Symres;
- }
-
- if (constraint->interface->kind != Ast_Kind_Interface) {
- // CLEANUP: This error message might not look totally right in some cases.
- ERROR_(constraint->token->pos, "'%b' is not an interface. It is a '%s'.",
- constraint->token->text, constraint->token->length,
- onyx_ast_node_kind_string(constraint->interface->kind));
- }
-
- bh_arr_new(global_heap_allocator, constraint->exprs, bh_arr_length(constraint->interface->exprs));
- bh_arr_each(InterfaceConstraint, ic, constraint->interface->exprs) {
- InterfaceConstraint new_ic = {0};
- new_ic.expr = (AstTyped *) ast_clone(context.ast_alloc, (AstNode *) ic->expr);
- new_ic.expected_type_expr = (AstType *) ast_clone(context.ast_alloc, (AstNode *) ic->expected_type_expr);
- new_ic.invert_condition = ic->invert_condition;
- bh_arr_push(constraint->exprs, new_ic);
- }
-
- assert(constraint->interface->entity && constraint->interface->entity->scope);
-
- constraint->scope = scope_create(context.ast_alloc, constraint->interface->entity->scope, constraint->token->pos);
-
- fori (i, 0, bh_arr_length(constraint->interface->params)) {
- InterfaceParam *ip = &constraint->interface->params[i];
-
- AstTyped *sentinel = onyx_ast_node_new(context.ast_alloc, sizeof(AstTyped), Ast_Kind_Constraint_Sentinel);
- sentinel->token = ip->value_token;
- sentinel->type_node = constraint->type_args[i];
-
- AstAlias *type_alias = onyx_ast_node_new(context.ast_alloc, sizeof(AstAlias), Ast_Kind_Alias);
- type_alias->token = ip->type_token;
- type_alias->alias = (AstTyped *) constraint->type_args[i];
-
- symbol_introduce(constraint->scope, ip->value_token, (AstNode *) sentinel);
- symbol_introduce(constraint->scope, ip->type_token, (AstNode *) type_alias);
- }
-
- assert(constraint->entity);
- constraint->entity->scope = constraint->scope;
-
- constraint->phase = Constraint_Phase_Checking_Expressions;
- return Check_Return_To_Symres;
- }
-
- case Constraint_Phase_Checking_Expressions: {
- fori (i, constraint->expr_idx, bh_arr_length(constraint->exprs)) {
- InterfaceConstraint* ic = &constraint->exprs[i];
-
- CheckStatus cs = check_expression(&ic->expr);
- if (cs == Check_Return_To_Symres || cs == Check_Yield_Macro) {
- return cs;
- }
-
- if (cs == Check_Error && !ic->invert_condition) {
- goto constraint_error;
- }
-
- if (cs == Check_Success && ic->invert_condition) {
- goto constraint_error;
- }
-
- if (ic->expected_type_expr) {
- cs = check_type(&ic->expected_type_expr);
- if (cs == Check_Return_To_Symres || cs == Check_Yield_Macro) {
- return cs;
- }
-
- ic->expected_type = type_build_from_ast(context.ast_alloc, ic->expected_type_expr);
- if (ic->expected_type == NULL) {
- YIELD(ic->expected_type_expr->token->pos, "Waiting on expected type expression to be resolved.");
- }
-
- TYPE_CHECK(&ic->expr, ic->expected_type) {
- if (!ic->invert_condition)
- goto constraint_error;
- }
- }
-
- constraint->expr_idx++;
- continue;
-
- constraint_error:
- // HACK HACK HACK
- onyx_clear_errors();
- *constraint->report_status = Constraint_Check_Status_Failed;
- return Check_Failed;
- }
-
- // HACK HACK HACK
- onyx_clear_errors();
- *constraint->report_status = Constraint_Check_Status_Success;
- return Check_Complete;
- }
- }
-
- return Check_Success;
-}
-
-CheckStatus check_constraint_context(ConstraintContext *cc, Scope *scope, OnyxFilePos pos) {
- if (cc->constraint_checks) {
- if (cc->constraints_met == 1) return Check_Success;
-
- fori (i, 0, bh_arr_length(cc->constraints)) {
- if (cc->constraint_checks[i] == Constraint_Check_Status_Failed) {
- if (cc->produce_errors) {
- AstConstraint *constraint = cc->constraints[i];
- char constraint_map[512] = {0};
- fori (i, 0, bh_arr_length(constraint->type_args)) {
- if (i != 0) strncat(constraint_map, ", ", 511);
-
- OnyxToken* symbol = constraint->interface->params[i].value_token;
- token_toggle_end(symbol);
- strncat(constraint_map, symbol->text, 511);
- token_toggle_end(symbol);
-
- strncat(constraint_map, " is of type '", 511);
- strncat(constraint_map, type_get_name(type_build_from_ast(context.ast_alloc, constraint->type_args[i])), 511);
- strncat(constraint_map, "'", 511);
- }
-
- onyx_report_error(constraint->exprs[constraint->expr_idx].expr->token->pos, Error_Critical, "Failed to satisfy constraint where %s.", constraint_map);
- onyx_report_error(constraint->token->pos, Error_Critical, "Here is where the interface was used.");
- onyx_report_error(pos, Error_Critical, "Here is the code that caused this constraint to be checked.");
-
- return Check_Error;
-
- } else {
- // If no error are suppose to be produced, we still need to signal that
- // the node reached a completed state.
- return Check_Failed;
- }
- }
-
- if (cc->constraint_checks[i] == Constraint_Check_Status_Queued) {
- YIELD(pos, "Waiting for constraints to be checked.");
- }
- }
-
- cc->constraints_met = 1;
- return Check_Success;
-
- } else {
- u32 count = bh_arr_length(cc->constraints);
- ConstraintCheckStatus *ccs = bh_alloc_array(context.ast_alloc, ConstraintCheckStatus, count);
-
- cc->constraint_checks = ccs;
-
- fori (i, 0, count) {
- ccs[i] = Constraint_Check_Status_Queued;
- cc->constraints[i]->report_status = &ccs[i];
- cc->constraints[i]->phase = Constraint_Phase_Cloning_Expressions;
-
- add_entities_for_node(NULL, (AstNode *) cc->constraints[i], scope, NULL);
- }
-
- return Check_Yield_Macro;
- }
-}
-
-CheckStatus check_polyquery(AstPolyQuery *query) {
- if (query->function_header->scope == NULL)
- query->function_header->scope = scope_create(context.ast_alloc, query->proc->parent_scope_of_poly_proc, query->token->pos);
-
- CheckStatus header_check = check_temp_function_header(query->function_header);
- if (header_check == Check_Return_To_Symres) return Check_Return_To_Symres;
-
- b32 solved_something = 0;
- i32 solved_count = 0;
- char *err_msg = NULL;
- bh_arr_each(AstPolyParam, param, query->proc->poly_params) {
- AstPolySolution sln;
- bh_arr_each(AstPolySolution, solved_sln, query->slns) {
- if (token_equals(param->poly_sym->token, solved_sln->poly_sym->token)) {
- goto poly_query_done;
- }
- }
-
- // CLEANUP: I think this can go away because it is already done in polymorph.c
- bh_arr_each(AstPolySolution, known_sln, query->proc->known_slns) {
- if (token_equals(param->poly_sym->token, known_sln->poly_sym->token)) {
- sln = *known_sln;
- goto poly_var_solved;
- }
- }
-
- TypeMatch result = find_polymorphic_sln(&sln, param, query->function_header, query->pp_lookup, query->given, &err_msg);
-
- switch (result) {
- case TYPE_MATCH_SUCCESS:
- goto poly_var_solved;
-
- case TYPE_MATCH_SPECIAL:
- return Check_Yield_Macro;
-
- case TYPE_MATCH_YIELD:
- case TYPE_MATCH_FAILED: {
- if (query->successful_symres || solved_something) continue;
-
- if (query->error_on_fail || context.cycle_detected) {
- onyx_report_error(query->token->pos, Error_Critical, "Error solving for polymorphic variable '%b'.", param->poly_sym->token->text, param->poly_sym->token->length);
- if (err_msg != NULL) onyx_report_error(query->token->pos, Error_Critical, "%s", err_msg);
- if (query->error_loc) onyx_report_error(query->error_loc->pos, Error_Critical, "Here is where the call is located."); // :ErrorMessage
- }
-
- return Check_Failed;
- }
- }
-
-poly_var_solved:
- solved_something = 1;
- bh_arr_push(query->slns, sln);
- insert_poly_sln_into_scope(query->function_header->scope, &sln);
-
-poly_query_done:
- solved_count += 1;
- }
-
- if (solved_count != bh_arr_length(query->proc->poly_params)) {
- if (solved_something || query->successful_symres) {
- return Check_Return_To_Symres;
- } else {
- return Check_Failed;
- }
- }
-
- return Check_Complete;
-}
-
-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_Temp_Function_Header: cs = check_temp_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); break;
- case Entity_Type_Constraint_Check: cs = check_constraint(ent->constraint); break;
- case Entity_Type_Polymorph_Query: cs = check_polyquery(ent->poly_query); break;
- case Entity_Type_Enum_Value: cs = check_expression(&ent->enum_value->value); break;
- case Entity_Type_Process_Directive: cs = check_process_directive((AstNode *) ent->expr); break;
-
- 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_File_Contents:
- if (context.options->no_file_contents) {
- onyx_report_error(ent->expr->token->pos, Error_Critical, "#file_contents is disabled for this compilation.");
- }
- break;
-
- default: break;
- }
-
- switch (cs) {
- case Check_Yield_Macro: ent->macro_attempts++; break;
- case Check_Success: ent->state = Entity_State_Code_Gen; goto clear_attempts;
- case Check_Complete: ent->state = Entity_State_Finalized; goto clear_attempts;
- case Check_Return_To_Symres: ent->state = Entity_State_Resolve_Symbols; goto clear_attempts;
- case Check_Failed: ent->state = Entity_State_Failed; goto clear_attempts;
-
- clear_attempts:
- ent->macro_attempts = 0;
- ent->micro_attempts = 0;
- }
-}
+++ /dev/null
-#include "astnodes.h"
-#include "parser.h"
-#include "utils.h"
-
-// Weird flags that shouldn't be used too often because they complicate things
-static b32 dont_copy_structs = 0;
-
-static inline b32 should_clone(AstNode* node) {
- if (node->flags & Ast_Flag_No_Clone) return 0;
-
- if (dont_copy_structs) {
- if (node->kind == Ast_Kind_Struct_Type) return 0;
- if (node->kind == Ast_Kind_Function) return 0;
- if (node->kind == Ast_Kind_Polymorphic_Proc) 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_Overloaded_Function:
- case Ast_Kind_Alias:
- case Ast_Kind_Code_Block:
- case Ast_Kind_Macro:
- case Ast_Kind_Symbol:
- case Ast_Kind_Poly_Struct_Type:
- case Ast_Kind_Basic_Type:
- case Ast_Kind_Enum_Type:
- case Ast_Kind_Enum_Value:
- 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_Load_All: 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(AstFunction);
- 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_Directive_Defined: return sizeof(AstDirectiveDefined);
- case Ast_Kind_Do_Block: return sizeof(AstDoBlock);
- case Ast_Kind_Constraint: return sizeof(AstConstraint);
- case Ast_Kind_Directive_Remove: return sizeof(AstDirectiveRemove);
- 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;
-}
-
-static bh_arr(AstNode *) captured_entities=NULL;
-#define E(ent) do { \
- assert(captured_entities); \
- ent->entity = NULL; \
- bh_arr_push(captured_entities, (AstNode *) ent); \
- } while (0);
-
-
-#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:
- C(AstCall, callee);
- 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);
- //fallthrough
-
- case Ast_Kind_Static_If:
- C(AstIfWhile, cond);
-
- C(AstIfWhile, true_stmt);
- C(AstIfWhile, false_stmt);
-
- if (nn->kind == Ast_Kind_Static_If) {
- ((AstIfWhile *) node)->flags |= Ast_Flag_Dead;
- ((AstIfWhile *) node)->flags |= Ast_Flag_Static_If_Resolved;
-
- ((AstIfWhile *) nn)->flags &= ~Ast_Flag_Dead;
- ((AstIfWhile *) nn)->flags &= ~Ast_Flag_Static_If_Resolved;
- E(nn);
- }
-
- break;
-
- case Ast_Kind_Switch_Case: {
- C(AstSwitchCase, block);
-
- AstSwitchCase *dw = (AstSwitchCase *) nn;
- AstSwitchCase *sw = (AstSwitchCase *) node;
-
- dw->values = NULL;
- bh_arr_new(global_heap_allocator, dw->values, bh_arr_length(sw->values));
- bh_arr_each(AstTyped *, value, sw->values)
- bh_arr_push(dw->values, (AstTyped *) ast_clone(a, *value));
-
- break;
- }
-
- case Ast_Kind_Switch: {
- AstSwitch* dw = (AstSwitch *) nn;
- AstSwitch* sw = (AstSwitch *) node;
-
- dw->initialization = ast_clone_list(a, sw->initialization);
- C(AstSwitch, expr);
-
-
- dw->cases = NULL;
- C(AstSwitch, case_block);
- 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->meta_tags = NULL;
- bh_arr_new(global_heap_allocator, ds->meta_tags, bh_arr_length(ss->meta_tags));
- bh_arr_each(AstTyped *, tag, ss->meta_tags) {
- bh_arr_push(ds->meta_tags, (AstTyped *) ast_clone(a, *tag));
- }
-
- if (ss->constraints.constraints) {
- memset(&ds->constraints, 0, sizeof(ConstraintContext));
- bh_arr_new(global_heap_allocator, ds->constraints.constraints, bh_arr_length(ss->constraints.constraints));
-
- bh_arr_each(AstConstraint *, constraint, ss->constraints.constraints) {
- bh_arr_push(ds->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
- }
- }
-
- ds->stcache = NULL;
- break;
- }
-
- case Ast_Kind_Struct_Member: {
- C(AstStructMember, type_node);
- C(AstStructMember, initial_value);
-
- AstStructMember *ds = (AstStructMember *) nn;
- AstStructMember *ss = (AstStructMember *) node;
-
- ds->meta_tags = NULL;
- bh_arr_new(global_heap_allocator, ds->meta_tags, bh_arr_length(ss->meta_tags));
- bh_arr_each(AstTyped *, tag, ss->meta_tags) {
- bh_arr_push(ds->meta_tags, (AstTyped *) ast_clone(a, *tag));
- }
-
- 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:
- case Ast_Kind_Polymorphic_Proc: {
- if (clone_depth > 1) {
- clone_depth--;
- return node;
- }
-
- AstFunction* df = (AstFunction *) nn;
- AstFunction* sf = (AstFunction *) node;
-
- convert_polyproc_to_function(df);
-
- if (sf->is_foreign) return node;
- assert(df->scope == NULL);
-
- df->nodes_that_need_entities_after_clone = NULL;
- bh_arr_new(global_heap_allocator, df->nodes_that_need_entities_after_clone, 1);
- captured_entities = df->nodes_that_need_entities_after_clone;
-
- df->return_type = (AstType *) ast_clone(a, sf->return_type);
- df->body = (AstBlock *) ast_clone(a, sf->body);
-
- df->nodes_that_need_entities_after_clone = captured_entities;
- captured_entities = NULL;
-
- df->params = NULL;
- bh_arr_new(context.ast_alloc, df->params, bh_arr_length(sf->params));
-
- bh_arr_each(AstParam, param, sf->params) {
- AstParam new_param = { 0 };
-
- dont_copy_structs = 1;
- new_param.local = (AstLocal *) ast_clone(a, param->local);
- new_param.local->flags &= ~Ast_Flag_Param_Symbol_Dirty;
- new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
- new_param.use_processed = 0;
- dont_copy_structs = 0;
-
- new_param.vararg_kind = param->vararg_kind;
- new_param.is_used = param->is_used;
- bh_arr_push(df->params, new_param);
- }
-
- if (sf->constraints.constraints) {
- memset(&df->constraints, 0, sizeof(ConstraintContext));
- bh_arr_new(context.ast_alloc, df->constraints.constraints, bh_arr_length(sf->constraints.constraints));
-
- bh_arr_each(AstConstraint *, constraint, sf->constraints.constraints) {
- bh_arr_push(df->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
- }
- }
-
- if (sf->tags) {
- bh_arr_new(context.ast_alloc, df->tags, bh_arr_length(sf->tags));
- bh_arr_each(AstTyped *, pexpr, sf->tags) {
- bh_arr_push(df->tags, (AstTyped *) ast_clone(a, (AstNode *) *pexpr));
- }
- }
-
- break;
- }
-
- case Ast_Kind_Constraint: {
- C(AstConstraint, interface);
-
- AstConstraint* dc = (AstConstraint *) nn;
- AstConstraint* sc = (AstConstraint *) node;
-
- dc->type_args = NULL;
- bh_arr_new(global_heap_allocator, dc->type_args, bh_arr_length(sc->type_args));
-
- bh_arr_each(AstType *, type_arg, sc->type_args) {
- bh_arr_push(dc->type_args, (AstType *) ast_clone(a, (AstNode *) *type_arg));
- }
-
- dc->phase = Constraint_Phase_Waiting_To_Be_Queued;
- 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 = (AstFunction *) 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_Directive_Defined:
- C(AstDirectiveDefined, expr);
- ((AstDirectiveDefined *) nn)->is_defined = 0;
- 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;
-
- case Ast_Kind_File_Contents:
- C(AstFileContents, filename_expr);
- E(nn);
- break;
- }
-
- clone_depth--;
- return nn;
-}
-
-#undef C
-
-AstFunction* clone_function_header(bh_allocator a, AstFunction* func) {
- if (func->kind != Ast_Kind_Function && func->kind != Ast_Kind_Polymorphic_Proc) return NULL;
-
- if (func->is_foreign) return func;
-
- AstFunction* new_func = onyx_ast_node_new(a, sizeof(AstFunction), func->kind);
- memmove(new_func, func, sizeof(AstFunction));
- assert(new_func->scope == NULL);
-
- convert_polyproc_to_function(new_func);
-
- 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;
-
- dont_copy_structs = 1;
- new_param.local = (AstLocal *) ast_clone(a, param->local);
- new_param.local->flags &= ~Ast_Flag_Param_Symbol_Dirty;
- new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
- new_param.use_processed = 0;
- dont_copy_structs = 0;
-
- new_param.vararg_kind = param->vararg_kind;
- new_param.is_used = param->is_used;
- bh_arr_push(new_func->params, new_param);
- }
-
- if (func->constraints.constraints) {
- memset(&new_func->constraints, 0, sizeof(ConstraintContext));
- bh_arr_new(global_heap_allocator, new_func->constraints.constraints, bh_arr_length(func->constraints.constraints));
-
- bh_arr_each(AstConstraint *, constraint, func->constraints.constraints) {
- bh_arr_push(new_func->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
- }
- }
-
- 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_Polymorphic_Proc && source->kind != Ast_Kind_Function) return;
-
- dest->nodes_that_need_entities_after_clone = NULL;
- bh_arr_new(global_heap_allocator, dest->nodes_that_need_entities_after_clone, 1);
- captured_entities = dest->nodes_that_need_entities_after_clone;
-
- dest->body = (AstBlock *) ast_clone(a, source->body);
-
- dest->nodes_that_need_entities_after_clone = captured_entities;
- captured_entities = NULL;
-}
+++ /dev/null
-#include "doc.h"
-#include "utils.h"
-#include "types.h"
-
-static i32 cmp_doc_entry(const void * a, const void * b) {
- DocEntry* d1 = (DocEntry *) a;
- DocEntry* d2 = (DocEntry *) b;
-
- return strncmp(d1->def, d2->def, 1024);
-}
-
-static i32 cmp_doc_package(const void * a, const void * b) {
- DocPackage* d1 = (DocPackage *) a;
- DocPackage* d2 = (DocPackage *) b;
-
- return strncmp(d1->name, d2->name, 1024);
-}
-
-static char* node_to_doc_def(const char* sym, AstNode *node, bh_allocator a) {
- static char buf[1024];
- memset(buf, 0, 1023);
-
- strncat(buf, sym, 1023);
- strncat(buf, " :: ", 1023);
-
- switch (node->kind) {
- case Ast_Kind_Function: {
- strncat(buf, "(", 1023);
-
- AstFunction *func = (AstFunction *) node;
- bh_arr_each(AstParam, param, func->params) {
- if (param != func->params)
- strncat(buf, ", ", 1023);
-
- token_toggle_end(param->local->token);
- strncat(buf, param->local->token->text, 1023);
- token_toggle_end(param->local->token);
-
- strncat(buf, ": ", 1023);
-
- strncat(buf, type_get_name(param->local->type), 1023);
-
- if (param->default_value != NULL) {
- strncat(buf, " = <default>", 1023);
- }
- }
-
- strncat(buf, ") -> ", 1023);
- strncat(buf, type_get_name(func->type->Function.return_type), 1023);
-
- break;
- }
-
- case Ast_Kind_Struct_Type: {
- strncat(buf, "struct { ", 1023);
-
- AstStructType* st = (AstStructType *) node;
- bh_arr_each(AstStructMember *, smem, st->members) {
-
- token_toggle_end((*smem)->token);
- strncat(buf, (*smem)->token->text, 1023);
- token_toggle_end((*smem)->token);
-
- strncat(buf, ": ", 1023);
-
- strncat(buf, type_get_name((*smem)->type), 1023);
-
- strncat(buf, "; ", 1023);
- }
-
- strncat(buf, "}", 1023);
- break;
- }
-
- case Ast_Kind_Basic_Type:
- case Ast_Kind_Array_Type:
- case Ast_Kind_Type_Alias:
- case Ast_Kind_Slice_Type:
- case Ast_Kind_DynArr_Type: {
- strncat(buf, type_get_name(type_build_from_ast(global_heap_allocator, (AstType *) node)), 1023);
- break;
- }
-
- default: {
- strncat(buf, "<unimplemented printing>", 1023);
- }
- }
-
- return bh_strdup(a, buf);
-}
-
-static DocPackage doc_package_create(Package* p, bh_allocator a) {
- DocPackage dp;
- dp.name = p->name;
- dp.public_entries = NULL;
- dp.private_entries = NULL;
-
- bh_arr_new(global_heap_allocator, dp.public_entries, 16);
- bh_arr_new(global_heap_allocator, dp.private_entries, 16);
-
- fori (i, 0, shlen(p->scope->symbols)) {
- char *key = p->scope->symbols[i].key;
- AstNode *value = p->scope->symbols[i].value;
-
- 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);
- }
-
- fori (i, 0, shlen(p->private_scope->symbols)) {
- char *key = p->scope->symbols[i].key;
- AstNode *value = p->scope->symbols[i].value;
-
- 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);
- }
-
- 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);
-
- fori (i, 0, shlen(context.packages)) {
- DocPackage dp = doc_package_create(context.packages[i].value, a);
- bh_arr_push(doc.package_docs, dp);
- }
-
- qsort(doc.package_docs, bh_arr_length(doc.package_docs), sizeof(DocPackage), cmp_doc_package);
-
- return doc;
-}
-
-static void onyx_docs_emit_human(OnyxDocumentation* doc, bh_file* file) {
- // NOTE: Disabling fancy line printing until I can make it better
- #if 0
- bh_arr_each(DocPackage, dp, doc->package_docs) {
- bh_printf("Package '%s'\n", dp->name);
-
- if (bh_arr_length(dp->public_entries) > 0) {
- bh_printf("\xe2\x94\x9c\xe2\x94\x80 Public symbols\n");
- bh_arr_each(DocEntry, de, dp->public_entries) {
- bh_printf("\xe2\x94\x82 \xe2\x94\x9c\xe2\x94\x80 %s\n", de->def);
-
- if (de->pos.filename != NULL)
- bh_printf("\xe2\x94\x82 \xe2\x94\x82 at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
- else
- bh_printf("\xe2\x94\x82 \xe2\x94\x82 compiler built-in\n");
-
- bh_printf("\xe2\x94\x82 \xe2\x94\x82\n");
- }
- }
-
- if (bh_arr_length(dp->private_entries) > 0) {
- bh_printf("\xe2\x94\x9c\xe2\x94\x80 Private symbols\n");
- bh_arr_each(DocEntry, de, dp->private_entries) {
- bh_printf("\xe2\x94\x82 \xe2\x94\x9c\xe2\x94\x80 %s\n", de->def);
- if (de->pos.filename != NULL)
- bh_printf("\xe2\x94\x82 \xe2\x94\x82 at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
- else
- bh_printf("\xe2\x94\x82 \xe2\x94\x82 compiler built-in\n");
-
- bh_printf("\xe2\x94\x82 \xe2\x94\x82\n");
- }
-
- bh_printf("\n");
- }
- }
- #else
- bh_arr_each(DocPackage, dp, doc->package_docs) {
- bh_fprintf(file, "Package '%s'\n", dp->name);
-
- if (bh_arr_length(dp->public_entries) > 0) {
- bh_fprintf(file, " Public symbols\n");
- bh_arr_each(DocEntry, de, dp->public_entries) {
- bh_fprintf(file, " %s\n", de->def);
-
- if (de->pos.filename != NULL)
- bh_fprintf(file, " at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
- else
- bh_fprintf(file, " compiler built-in\n");
-
- bh_fprintf(file, " \n");
- }
- }
-
- if (bh_arr_length(dp->private_entries) > 0) {
- bh_fprintf(file, " Private symbols\n");
- bh_arr_each(DocEntry, de, dp->private_entries) {
- bh_fprintf(file, " %s\n", de->def);
- if (de->pos.filename != NULL)
- bh_fprintf(file, " at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
- else
- bh_fprintf(file, " compiler built-in\n");
-
- bh_fprintf(file, " \n");
- }
-
- bh_fprintf(file, "\n");
- }
- }
- #endif
-}
-
-
-static i32 sort_tags(const void* a, const void* b) {
- DocEntry *da = *((DocEntry **) a);
- DocEntry *db = *((DocEntry **) b);
-
- return strcmp(da->sym, db->sym);
-}
-
-static void onyx_docs_emit_tags(OnyxDocumentation* doc) {
- bh_file tags_file;
- if (bh_file_create(&tags_file, "./tags") != BH_FILE_ERROR_NONE) {
- bh_printf("Cannot create 'tags'.\n");
- return;
- }
-
- bh_fprintf(&tags_file, "!_TAG_FILE_FORMAT\t2\n");
- bh_fprintf(&tags_file, "!_TAG_FILE_SORTED\t1\n");
- bh_fprintf(&tags_file, "!_TAG_OUTPUT_FILESEP\tslash\n");
- bh_fprintf(&tags_file, "!_TAG_OUTPUT_MODE\tu-ctags\n");
- bh_fprintf(&tags_file, "!_TAG_PROGRAM_AUTHOR\tOnyx Compiler\n");
- bh_fprintf(&tags_file, "!_TAG_PROGRAM_NAME\tOnyx Compiler\n");
- bh_fprintf(&tags_file, "!_TAG_PROGRAM_URL\thttps://github.com/brendanfh/onyx\n");
- bh_fprintf(&tags_file, "!_TAG_PROGRAM_VERSION\t0.0.1\n");
-
- bh_arr(DocEntry *) tags = NULL;
- bh_arr_new(global_heap_allocator, tags, 256);
-
- bh_arr_each(DocPackage, dp, doc->package_docs) {
- bh_arr_each(DocEntry, de, dp->public_entries) bh_arr_push(tags, de);
- bh_arr_each(DocEntry, de, dp->private_entries) bh_arr_push(tags, de);
- }
-
- qsort(tags, bh_arr_length(tags), sizeof(DocEntry *), sort_tags);
-
- bh_arr_each(DocEntry *, tag, tags) {
- if ((*tag)->pos.filename == NULL) continue;
-
- i32 line_len = 0;
- char *c = (*tag)->pos.line_start;
- while (*c++ != '\n') line_len++;
-
- bh_fprintf(&tags_file, "%s\t%s\t/^%b$/\n",
- (*tag)->sym, (*tag)->pos.filename,
- (*tag)->pos.line_start, line_len);
- }
-
- bh_file_close(&tags_file);
-}
-
-static void onyx_docs_emit_html(OnyxDocumentation* doc, bh_file* file) {
- bh_fprintf(file, "HTML documentation output not supported yet.\n");
- return;
-}
-
-void onyx_docs_emit(OnyxDocumentation* doc, const char* filename) {
- bh_file file;
- if (bh_file_create(&file, filename) != BH_FILE_ERROR_NONE) {
- bh_printf("ERROR: Failed to open file '%s' for writing documentation.\n", filename);
- return;
- }
-
- switch (doc->format) {
- case Doc_Format_Human: onyx_docs_emit_human(doc, &file); break;
- case Doc_Format_Html: onyx_docs_emit_html(doc, &file); break;
- case Doc_Format_Tags: onyx_docs_emit_tags(doc); break;
- }
-
- bh_file_close(&file);
-}
-
+++ /dev/null
-#include "bh.h"
-#include "astnodes.h"
-#include "utils.h"
-
-static inline i32 entity_phase(Entity* e1) {
- if (e1->state <= Entity_State_Parse && e1->macro_attempts == 0) 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) {
- while (1) {
- 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;
-
- index = min_index;
- continue;
- }
-
- break;
- }
-}
-
-void entity_heap_init(EntityHeap* entities) {
- bh_arena_init(&entities->entity_arena, global_heap_allocator, 32 * 1024);
-}
-
-static u32 next_entity_id = 0;
-
-// 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->id = next_entity_id++;
- 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);
-}
-
-void entity_change_type(EntityHeap* entities, Entity *ent, EntityType new_type) {
- entities->type_count[ent->type]--;
- entities->type_count[new_type]++;
- ent->type = new_type;
-}
-
-// 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); \
- } \
-
- if (node->entity != NULL) return;
-
- 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_All:
- case Ast_Kind_Load_File: {
- ent.type = Entity_Type_Load_File;
- ent.include = (AstInclude *) node;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Library_Path:
- case Ast_Kind_Load_Path: {
- 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 (((AstFunction *) node)->is_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: {
- 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_Enum_Value: {
- ent.type = Entity_Type_Enum_Value;
- ent.state = Entity_State_Check_Types;
- ent.enum_value = (AstEnumValue *) 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);
- ((AstMemRes *) node)->type_entity = entity;
-
- 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 = (AstFunction *) node;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Polymorph_Query: {
- ent.type = Entity_Type_Polymorph_Query;
- ent.state = Entity_State_Check_Types;
- ent.poly_query = (AstPolyQuery *) 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:
- case Ast_Kind_Directive_Init:
- case Ast_Kind_Directive_Library:
- case Ast_Kind_Injection: {
- 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;
- }
-
- case Ast_Kind_Interface: {
- ent.type = Entity_Type_Interface;
- ent.interface = (AstInterface *) node;
- ent.state = Entity_State_Resolve_Symbols;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Constraint: {
- ent.type = Entity_Type_Constraint_Check;
- ent.constraint = (AstConstraint *) node;
- ent.state = Entity_State_Resolve_Symbols;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Foreign_Block: {
- ent.type = Entity_Type_Foreign_Block;
- ent.foreign_block = (AstForeignBlock *) node;
- ent.state = Entity_State_Resolve_Symbols;
- ENTITY_INSERT(ent);
- break;
- }
-
- default: {
- ent.type = Entity_Type_Expression;
- ent.expr = (AstTyped *) node;
- ENTITY_INSERT(ent);
- break;
- }
- }
-
- node->entity = entity;
-}
+++ /dev/null
-#include "errors.h"
-#include "utils.h"
-
-OnyxErrors errors;
-
-void onyx_errors_init(bh_arr(bh_file_contents)* files) {
- errors.file_contents = files;
-
- bh_arena_init(&errors.msg_arena, global_heap_allocator, 16 * 1024);
- errors.msg_alloc = bh_arena_allocator(&errors.msg_arena);
-
- bh_arr_new(global_heap_allocator, errors.errors, 4);
-}
-
-static void print_detailed_message(OnyxError* err, bh_file_contents* fc) {
- bh_printf("(%s:%l,%l) %s\n", err->pos.filename, err->pos.line, err->pos.column, err->text);
-
- b32 colored_printing = 0;
- #ifdef _BH_LINUX
- colored_printing = !context.options->no_colors;
- #endif
-
- i32 linelength = 0;
- i32 first_char = 0;
- char* walker = err->pos.line_start;
- while (*walker == ' ' || *walker == '\t') first_char++, linelength++, walker++;
- while (*walker != '\n') linelength++, walker++;
-
- if (colored_printing) bh_printf("\033[90m");
- i32 numlen = bh_printf(" %d | ", err->pos.line);
- if (colored_printing) bh_printf("\033[94m");
- bh_printf("%b\n", err->pos.line_start, linelength);
-
- char* pointer_str = bh_alloc_array(global_scratch_allocator, char, linelength + numlen);
- memset(pointer_str, ' ', linelength + 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[0m");
-}
-
-static i32 errors_sort(const void* v1, const void* v2) {
- OnyxError* e1 = (OnyxError *) v1;
- OnyxError* e2 = (OnyxError *) v2;
- return e2->rank - e1->rank;
-}
-
-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
-
- qsort(errors.errors, bh_arr_length(errors.errors), sizeof(OnyxError), errors_sort);
-
- OnyxErrorRank last_rank = errors.errors[0].rank;
- bh_arr_each(OnyxError, err, errors.errors) {
- if (last_rank != err->rank) break;
-
- 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);
- }
-
- last_rank = err->rank;
- }
-}
-
-b32 onyx_has_errors() {
- return bh_arr_length(errors.errors) > 0;
-}
-
-void onyx_clear_errors() {
- if (context.cycle_detected) return;
-
- bh_arr_set_length(errors.errors, 0);
-}
-
-void onyx_submit_error(OnyxError error) {
- bh_arr_push(errors.errors, error);
-}
-
-void onyx_report_error(OnyxFilePos pos, OnyxErrorRank rank, char * format, ...) {
-
- va_list vargs;
- va_start(vargs, format);
- char* msg = bh_bprintf_va(format, vargs);
- va_end(vargs);
-
- OnyxError err = {
- .pos = pos,
- .rank = rank,
- .text = bh_strdup(errors.msg_alloc, msg),
- };
-
- bh_arr_push(errors.errors, err);
-}
-
-void onyx_submit_warning(OnyxError error) {
- bh_file_contents file_contents = { 0 };
- bh_arr_each(bh_file_contents, fc, *errors.file_contents) {
- if (!strcmp(fc->filename, error.pos.filename)) {
- file_contents = *fc;
- break;
- }
- }
-
- print_detailed_message(&error, &file_contents);
-}
-
-// This definitely doesn't do what I thought it did?
-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,
- .rank = Error_Warning,
- .text = msg,
- };
-
- bh_arr_push(errors.errors, err);
-
- /*
-
- bh_file_contents file_contents = { 0 };
- bh_arr_each(bh_file_contents, fc, *errors.file_contents) {
- if (!strcmp(fc->filename, pos.filename)) {
- file_contents = *fc;
- break;
- }
- }
-
- print_detailed_message(&err, &file_contents);
- */
-}
+++ /dev/null
-#include "bh.h"
-#include "lex.h"
-#include "utils.h"
-#include "errors.h"
-
-u64 lexer_lines_processed = 0;
-u64 lexer_tokens_processed = 0;
-
-static const char* token_type_names[] = {
- "TOKEN_TYPE_UNKNOWN",
- "TOKEN_TYPE_END_STREAM",
-
- "TOKEN_TYPE_COMMENT",
-
- "package",
- "struct",
- "enum",
- "use",
- "if",
- "else",
- "elseif",
- "return",
- "global",
- "cast",
- "while",
- "for",
- "break",
- "continue",
- "sizeof",
- "alignof",
- "typeof",
- "defer",
- "do",
- "case",
- "switch",
- "fallthrough",
- "macro",
- "interface",
- "where",
-
- "->",
- "<-",
- "---",
- "|>",
-
- ">=",
- "<=",
- "==",
- "!=",
- "+=",
- "-=",
- "*=",
- "/=",
- "%=",
- "&=",
- "|=",
- "^=",
- "&&",
- "||",
- "<<",
- ">>",
- ">>>",
- "<<=",
- ">>=",
- ">>>=",
- "..",
- "~~",
-
- "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;
- }
-
- // She-bang
- if (tokenizer->curr == tokenizer->start) {
- if (*tokenizer->curr == '#' && *(tokenizer->curr + 1) == '!') {
- tk.type = Token_Type_Comment;
-
- tokenizer->curr += 2;
- while (*tokenizer->curr++ != '\n' && tokenizer->curr != tokenizer->end);
-
- tk.length = tokenizer->curr - tk.text;
- 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;
- }
-
- if (*tk.text == '"' && *(tk.text + 1) == '"' && *(tk.text + 2) == '"') {
- u64 len = 0;
-
- INCREMENT_CURR_TOKEN(tokenizer);
- INCREMENT_CURR_TOKEN(tokenizer);
- INCREMENT_CURR_TOKEN(tokenizer);
-
- while (!(*tokenizer->curr == '"' && *(tokenizer->curr + 1) == '"' && *(tokenizer->curr + 2) == '"')) {
- len++;
- INCREMENT_CURR_TOKEN(tokenizer);
- }
-
- INCREMENT_CURR_TOKEN(tokenizer);
- INCREMENT_CURR_TOKEN(tokenizer);
- INCREMENT_CURR_TOKEN(tokenizer);
-
- tk.text += 3;
- tk.length = len;
- tk.type = Token_Type_Literal_String;
- 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);
- 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);
- LITERAL_TOKEN("interface", 1, Token_Type_Keyword_Interface);
- break;
- case 'm':
- LITERAL_TOKEN("macro", 1, Token_Type_Keyword_Macro);
- break;
- case 'p':
- LITERAL_TOKEN("package", 1, Token_Type_Keyword_Package);
- 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);
- LITERAL_TOKEN("where", 1, Token_Type_Keyword_Where);
- 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_Less_Equal);
- LITERAL_TOKEN("<-", 0, Token_Type_Right_Arrow);
- LITERAL_TOKEN("<<=", 0, Token_Type_Shl_Equal);
- LITERAL_TOKEN("<<", 0, Token_Type_Shift_Left);
- break;
-
- case '>':
- LITERAL_TOKEN(">=", 0, Token_Type_Greater_Equal);
- 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);
- 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) || *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;
-}
-
-b32 token_same_file(OnyxToken *tkn1, OnyxToken *tkn2) {
- if (!tkn1 || !tkn2) return 0;
-
- if (tkn1->pos.filename == tkn2->pos.filename) return 1;
-
- // :Security?
- return strcmp(tkn1->pos.filename, tkn2->pos.filename) == 0;
-}
+++ /dev/null
-// #define BH_DEBUG
-#define BH_DEFINE
-#define BH_NO_TABLE
-#define STB_DS_IMPLEMENTATION
-#include "bh.h"
-
-#include "lex.h"
-#include "errors.h"
-#include "parser.h"
-#include "utils.h"
-#include "wasm_emit.h"
-#include "doc.h"
-
-#define VERSION "v0.1.0"
-
-
-// #ifndef CORE_INSTALLATION
-// #ifdef _BH_LINUX
-// #define CORE_INSTALLATION "/usr/share/onyx"
-// #elif defined(_WIN32) || defined(_WIN64)
-// #define CORE_INSTALLATION "\\dev\\onyx\\"
-// #endif
-// #endif
-
-
-
-
-Context context;
-
-
-static const char* docstring = "Onyx compiler version " VERSION "\n"
- "\n"
- "The compiler for the Onyx programming language, created by Brendan Hansen.\n"
- "\n"
- "Usage:\n"
- "\tonyx compile [-o <target file>] [--verbose] <input files>\n"
-#ifdef ENABLE_RUN_WITH_WASMER
- "\tonyx run <input files> -- <args>\n"
-#endif
- // "\tonyx doc <input files>\n"
- "\tonyx help\n"
- "\n"
- "Flags:\n"
- "\t<input files> List of initial files\n"
- "\t-o <target_file> Specify the target file (default: out.wasm).\n"
- "\t--runtime, -r <runtime> Specifies the runtime. Can be: onyx, wasi, js, custom.\n"
- "\t--verbose, -V Verbose output.\n"
- "\t -VV Very verbose output.\n"
- "\t -VVV Very very verbose output (to be used by compiler developers).\n"
- "\t--wasm-mvp Use only WebAssembly MVP features.\n"
- "\t--multi-threaded Enables multi-threading for this compilation.\n"
- "\t--doc <doc_file>\n"
- "\t--generate-foreign-info\n"
- "\n"
- "Developer flags:\n"
- "\t--print-function-mappings Prints a mapping from WASM function index to source location.\n"
- "\t--print-static-if-results Prints the conditional result of each #if statement. Useful for debugging.\n"
- "\t--print-notes Prints the location of notes throughout the loaded code.\n"
- "\t--no-colors Disables colors in the error message.\n"
- "\t--no-file-contents Disables '#file_contents' for security.\n"
- "\n";
-
-
-static CompileOptions compile_opts_parse(bh_allocator alloc, int argc, char *argv[]) {
- CompileOptions options = {
- .allocator = alloc,
- .action = ONYX_COMPILE_ACTION_PRINT_HELP,
-
- .verbose_output = 0,
- .fun_output = 0,
- .print_function_mappings = 0,
- .no_file_contents = 0,
-
- .use_post_mvp_features = 1,
- .use_multi_threading = 0,
-
- .runtime = Runtime_Onyx,
-
- .files = NULL,
- .target_file = "out.wasm",
-
- .documentation_file = NULL,
-
- .debug_enabled = 0,
-
- .passthrough_argument_count = 0,
- .passthrough_argument_data = NULL,
- };
-
- bh_arr_new(alloc, options.files, 2);
- bh_arr_new(alloc, options.included_folders, 2);
-
- char* core_installation;
- #ifdef _BH_LINUX
- core_installation = CORE_INSTALLATION;
- #endif
- #ifdef _BH_WINDOWS
- core_installation = bh_alloc_array(global_heap_allocator, u8, 512);
- GetEnvironmentVariableA("ONYX_PATH", core_installation, 512);
- #endif
-
- // NOTE: Add the current folder
- bh_arr_push(options.included_folders, core_installation);
- bh_arr_push(options.included_folders, ".");
-
- if (argc == 1) return options;
- i32 arg_parse_start = 1;
-
- if (!strcmp(argv[1], "help")) options.action = ONYX_COMPILE_ACTION_PRINT_HELP;
- if (!strcmp(argv[1], "compile")) {
- options.action = ONYX_COMPILE_ACTION_COMPILE;
- arg_parse_start = 2;
- }
- #ifdef ENABLE_RUN_WITH_WASMER
- else if (!strcmp(argv[1], "run")) {
- options.action = ONYX_COMPILE_ACTION_RUN;
- arg_parse_start = 2;
- }
- #endif
- else options.action = ONYX_COMPILE_ACTION_COMPILE;
-
- if (options.action != ONYX_COMPILE_ACTION_PRINT_HELP) {
- fori(i, arg_parse_start, argc) {
- if (!strcmp(argv[i], "-o")) {
- options.target_file = argv[++i];
- }
- else if (!strcmp(argv[i], "--verbose") || !strcmp(argv[i], "-V")) {
- options.verbose_output = 1;
- }
- else if (!strcmp(argv[i], "-VV")) {
- options.verbose_output = 2;
- }
- else if (!strcmp(argv[i], "-VVV")) {
- options.verbose_output = 3;
- }
- else if (!strcmp(argv[i], "--print-function-mappings")) {
- options.print_function_mappings = 1;
- }
- else if (!strcmp(argv[i], "--print-static-if-results")) {
- options.print_static_if_results = 1;
- }
- else if (!strcmp(argv[i], "--print-notes")) {
- options.print_notes = 1;
- }
- else if (!strcmp(argv[i], "--no-colors")) {
- options.no_colors = 1;
- }
- else if (!strcmp(argv[i], "--no-file-contents")) {
- options.no_file_contents = 1;
- }
- else if (!strcmp(argv[i], "--wasm-mvp")) {
- options.use_post_mvp_features = 0;
- }
- else if (!strcmp(argv[i], "--multi-threaded")) {
- options.use_multi_threading = 1;
- }
- else if (!strcmp(argv[i], "--generate-foreign-info")) {
- options.generate_foreign_info = 1;
- }
- else if (!strcmp(argv[i], "-I")) {
- bh_arr_push(options.included_folders, argv[++i]);
- }
- else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--runtime")) {
- i += 1;
- if (!strcmp(argv[i], "onyx")) options.runtime = Runtime_Onyx;
- else if (!strcmp(argv[i], "wasi")) options.runtime = Runtime_Wasi;
- else if (!strcmp(argv[i], "js")) options.runtime = Runtime_Js;
- else if (!strcmp(argv[i], "custom")) options.runtime = Runtime_Custom;
- else {
- bh_printf("WARNING: '%s' is not a valid runtime. Defaulting to 'onyx'.\n", argv[i]);
- options.runtime = Runtime_Wasi;
- }
- }
- else if (!strcmp(argv[i], "--doc")) {
- options.documentation_file = argv[++i];
- }
- else if (!strcmp(argv[i], "--debug")) {
- options.debug_enabled = 1;
- }
- else if (!strcmp(argv[i], "--")) {
- options.passthrough_argument_count = argc - i - 1;
- options.passthrough_argument_data = &argv[i + 1];
- break;
- }
-#if defined(_BH_LINUX)
- // NOTE: Fun output is only enabled for Linux because Windows command line
- // is not ANSI compatible and for a silly feature, I don't want to learn
- // how to properly do arbitrary graphics in it.
- else if (!strcmp(argv[i], "--fun") || !strcmp(argv[i], "-F")) {
- options.fun_output = 1;
- }
-#endif
- else {
- bh_arr_push(options.files, argv[i]);
- }
- }
- }
-
- // NOTE: Always enable multi-threading for the Onyx runtime.
- if (options.runtime == Runtime_Onyx) {
- options.use_multi_threading = 1;
- }
-
- return options;
-}
-
-static void compile_opts_free(CompileOptions* opts) {
- bh_arr_free(opts->files);
- bh_arr_free(opts->included_folders);
-}
-
-
-
-
-
-typedef enum CompilerProgress {
- ONYX_COMPILER_PROGRESS_ERROR,
- ONYX_COMPILER_PROGRESS_FAILED_OUTPUT,
- ONYX_COMPILER_PROGRESS_SUCCESS
-} CompilerProgress;
-
-static OnyxToken implicit_load_token = { '#', 1, 0, { 0, 0, 0, 0, 0 } };
-static AstInclude* create_load(bh_allocator alloc, char* filename) {
- AstInclude* include_node = onyx_ast_node_new(alloc, sizeof(AstInclude), Ast_Kind_Load_File);
- include_node->name = filename;
- include_node->token = &implicit_load_token;
-
- return include_node;
-}
-
-// HACK
-static u32 special_global_entities_remaining = 3;
-static Entity *runtime_info_types_entity;
-static Entity *runtime_info_foreign_entity;
-static Entity *runtime_info_proc_tags_entity;
-
-static void context_init(CompileOptions* opts) {
- types_init();
-
- context.options = opts;
- context.cycle_detected = 0;
- context.cycle_almost_detected = 0;
-
- OnyxFilePos internal_location = { 0 };
- internal_location.filename = "<compiler internal>";
- internal_location.line = 1;
- internal_location.column = 1;
- context.global_scope = scope_create(global_heap_allocator, NULL, internal_location);
- sh_new_arena(context.packages);
-
- // NOTE: This will be initialized upon the first call to entity_heap_insert.
- context.entities.next_id = 0;
- context.entities.entities = NULL;
-
- onyx_errors_init(&context.loaded_files);
-
- context.token_alloc = global_heap_allocator;
-
- // NOTE: Create the arena where tokens and AST nodes will exist
- // Prevents nodes from being scattered across memory due to fragmentation
- bh_arena_init(&context.ast_arena, global_heap_allocator, 16 * 1024 * 1024); // 16MB
- context.ast_alloc = bh_arena_allocator(&context.ast_arena);
-
- context.wasm_module = bh_alloc_item(global_heap_allocator, OnyxWasmModule);
- *context.wasm_module = onyx_wasm_module_create(global_heap_allocator);
-
- entity_heap_init(&context.entities);
-
- // NOTE: Add builtin entities to pipeline.
- entity_heap_insert(&context.entities, ((Entity) {
- .state = Entity_State_Parse_Builtin,
- .type = Entity_Type_Load_File,
- .package = NULL,
- .include = create_load(context.ast_alloc, "core/builtin"),
- }));
-
- entity_heap_insert(&context.entities, ((Entity) {
- .state = Entity_State_Parse_Builtin,
- .type = Entity_Type_Load_File,
- .package = NULL,
- .include = create_load(context.ast_alloc, "core/runtime/build_opts"),
- }));
-
- if (context.options->runtime != Runtime_Custom) {
- runtime_info_types_entity = entity_heap_insert(&context.entities, ((Entity) {
- .state = Entity_State_Parse,
- .type = Entity_Type_Load_File,
- .package = NULL,
- .include = create_load(context.ast_alloc, "core/runtime/info/types"),
- }));
- runtime_info_foreign_entity = entity_heap_insert(&context.entities, ((Entity) {
- .state = Entity_State_Parse,
- .type = Entity_Type_Load_File,
- .package = NULL,
- .include = create_load(context.ast_alloc, "core/runtime/info/foreign_blocks"),
- }));
- runtime_info_proc_tags_entity = entity_heap_insert(&context.entities, ((Entity) {
- .state = Entity_State_Parse,
- .type = Entity_Type_Load_File,
- .package = NULL,
- .include = create_load(context.ast_alloc, "core/runtime/info/proc_tags"),
- }));
- }
-
- add_entities_for_node(NULL, (AstNode *) &builtin_stack_top, context.global_scope, NULL);
- add_entities_for_node(NULL, (AstNode *) &builtin_heap_start, context.global_scope, NULL);
- add_entities_for_node(NULL, (AstNode *) &builtin_tls_base, context.global_scope, NULL);
- add_entities_for_node(NULL, (AstNode *) &builtin_tls_size, context.global_scope, NULL);
-
- // NOTE: Add all files passed by command line to the queue
- bh_arr_each(const char *, filename, opts->files) {
- AstInclude* load_node = create_load(context.ast_alloc, (char *) *filename);
- add_entities_for_node(NULL, (AstNode *) load_node, context.global_scope, NULL);
- }
-}
-
-static void context_free() {
- bh_arena_free(&context.ast_arena);
- bh_arr_free(context.loaded_files);
-
- compile_opts_free(context.options);
-}
-
-static void parse_source_file(bh_file_contents* file_contents) {
- // :Remove passing the allocators as parameters
- OnyxTokenizer tokenizer = onyx_tokenizer_create(context.token_alloc, file_contents);
- onyx_lex_tokens(&tokenizer);
-
- file_contents->line_count = tokenizer.line_number;
-
- OnyxParser parser = onyx_parser_create(context.ast_alloc, &tokenizer);
- onyx_parse(&parser);
- onyx_parser_free(&parser);
-}
-
-static b32 process_source_file(char* filename, OnyxFilePos error_pos) {
- bh_arr_each(bh_file_contents, fc, context.loaded_files) {
- // Duplicates are detected here and since these filenames will be the full path,
- // string comparing them should be all that is necessary.
- if (!strcmp(fc->filename, filename)) return 1;
- }
-
- bh_file file;
- bh_file_error err = bh_file_open(&file, filename);
- if (err != BH_FILE_ERROR_NONE) {
- if (context.cycle_detected) {
- onyx_report_error(error_pos, Error_Critical, "Failed to open file %s", filename);
- }
- return 0;
- }
-
- bh_file_contents fc = bh_file_read_contents(context.token_alloc, &file);
- bh_file_close(&file);
-
- bh_arr_push(context.loaded_files, fc);
-
- if (context.options->verbose_output == 2)
- bh_printf("Processing source file: %s (%d bytes)\n", file.filename, fc.length);
-
- parse_source_file(&bh_arr_last(context.loaded_files));
- return 1;
-}
-
-static b32 process_load_entity(Entity* ent) {
- assert(ent->type == Entity_Type_Load_File || ent->type == Entity_Type_Load_Path);
- AstInclude* include = ent->include;
-
- if (include->kind == Ast_Kind_Load_File) {
- // :RelativeFiles
- const char* parent_file = include->token->pos.filename;
- if (parent_file == NULL) parent_file = ".";
-
- char* parent_folder = bh_path_get_parent(parent_file, global_scratch_allocator);
-
- char* filename = bh_lookup_file(include->name, parent_folder, ".onyx", 1, context.options->included_folders, 1);
- char* formatted_name = bh_strdup(global_heap_allocator, filename);
-
- return process_source_file(formatted_name, include->token->pos);
-
- } else if (include->kind == Ast_Kind_Load_All) {
- const char* parent_file = include->token->pos.filename;
- if (parent_file == NULL) parent_file = ".";
-
- char* parent_folder = bh_path_get_parent(parent_file, global_scratch_allocator);
- char folder[512];
- if (bh_str_starts_with(include->name, "./")) {
- bh_snprintf(folder, 511, "%s/%s", parent_folder, include->name + 2);
- } else {
- bh_snprintf(folder, 511, "%s", include->name);
- }
-
- // This does not take into account #load_path'd folders...
-
- bh_path_convert_separators(folder);
- bh_dir dir = bh_dir_open(folder);
- if (dir == NULL) {
- onyx_report_error(include->token->pos, Error_Critical, "Could not find folder '%s'.", folder);
- return 0;
- }
-
- bh_dirent entry;
- b32 success = 1;
- char fullpath[512];
- while (bh_dir_read(dir, &entry)) {
- if (entry.type == BH_DIRENT_FILE && bh_str_ends_with(entry.name, ".onyx")) {
- bh_snprintf(fullpath, 511, "%s/%s", folder, entry.name);
- bh_path_convert_separators(fullpath);
- u8* formatted_name = bh_path_get_full_name(fullpath, global_heap_allocator);
- success = process_source_file(formatted_name, include->token->pos);
- if (!success) break;
- }
- }
-
- bh_dir_close(dir);
- return success;
-
- } else if (include->kind == Ast_Kind_Load_Path) {
- bh_arr_push(context.options->included_folders, include->name);
-
- } else if (include->kind == Ast_Kind_Library_Path) {
- bh_arr_push(context.wasm_module->library_paths, include->name);
- }
-
- return 1;
-}
-
-static b32 process_entity(Entity* ent) {
- static char verbose_output_buffer[512];
- if (context.options->verbose_output == 3) {
- if (ent->expr && ent->expr->token)
- snprintf(verbose_output_buffer, 511,
- "%20s | %24s (%d, %d) | %s:%i:%i \n",
- entity_state_strings[ent->state],
- entity_type_strings[ent->type],
- (u32) ent->macro_attempts,
- (u32) ent->micro_attempts,
- ent->expr->token->pos.filename,
- ent->expr->token->pos.line,
- ent->expr->token->pos.column);
-
- else if (ent->expr)
- snprintf(verbose_output_buffer, 511,
- "%20s | %24s (%d, %d) \n",
- entity_state_strings[ent->state],
- entity_type_strings[ent->type],
- (u32) ent->macro_attempts,
- (u32) ent->micro_attempts);
- }
-
- // CLEANUP: There should be a nicer way to track if the builtins have
- // already been initialized.
- static b32 builtins_initialized = 0;
-
- EntityState before_state = ent->state;
- switch (before_state) {
- case Entity_State_Error:
- if (ent->type != Entity_Type_Error) {
- onyx_report_error(ent->expr->token->pos, Error_Critical, "Error entity unexpected. This is definitely a compiler bug");
- } else {
- onyx_report_error(ent->error->token->pos, Error_Critical, "Static error occured: '%b'", ent->error->error_msg->text, ent->error->error_msg->length);
- }
- break;
-
- case Entity_State_Parse_Builtin:
- process_load_entity(ent);
- ent->state = Entity_State_Finalized;
- break;
-
- case Entity_State_Introduce_Symbols:
- // Currently, introducing symbols is handled in the symbol resolution
- // function. Maybe there should be a different place where that happens?
- symres_entity(ent);
- break;
-
- case Entity_State_Parse:
- if (!builtins_initialized) {
- builtins_initialized = 1;
- initialize_builtins(context.ast_alloc);
- introduce_build_options(context.ast_alloc);
- }
-
- // GROSS
- if (special_global_entities_remaining == 0) {
- special_global_entities_remaining--;
- initalize_special_globals();
- }
-
- if (process_load_entity(ent)) {
- // GROSS
- if (ent == runtime_info_types_entity
- || ent == runtime_info_proc_tags_entity
- || ent == runtime_info_foreign_entity) {
- special_global_entities_remaining--;
- }
-
- ent->state = Entity_State_Finalized;
- } else {
- ent->macro_attempts++;
- }
- break;
-
- 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;
- }
-
- b32 changed = ent->state != before_state;
- if (context.options->verbose_output == 3) {
- if (changed) printf("SUCCESS to %20s | %s", entity_state_strings[ent->state], verbose_output_buffer);
- else printf("YIELD to %20s | %s", entity_state_strings[ent->state], verbose_output_buffer);
- }
-
- return changed;
-}
-
-// Just having fun with some visual output - brendanfh 2020/12/14
-#if defined(_BH_LINUX)
-static void output_dummy_progress_bar() {
- EntityHeap* eh = &context.entities;
- if (bh_arr_length(eh->entities) == 0) return;
-
- static const char* state_colors[] = {
- "\e[91m", "\e[93m", "\e[94m", "\e[93m",
- "\e[97m", "\e[95m", "\e[96m", "\e[92m", "\e[91m",
- };
-
- printf("\e[2;1H");
-
- for (i32 i = 0; i < Entity_State_Count - 1; i++) {
- if (i % 2 == 0) printf("\n");
- printf("%s %25s \xe2\x96\x88 ", state_colors[i], entity_state_strings[i]);
- }
-
- printf("\n\n");
-
- for (i32 i = 0; i < Entity_Type_Count; i++) {
- if (eh->type_count[i] == 0) printf("\e[90m");
- else if ((i32) eh->entities[0]->type == i) printf("\e[92m");
- else printf("\e[97m");
-
- printf("%25s (%4d) | ", entity_type_strings[i], eh->type_count[i]);
-
- printf("\e[0K");
- for (i32 j = 0; j < Entity_State_Count; j++) {
- if (eh->all_count[j][i] == 0) continue;
-
- printf("%s", state_colors[j]);
-
- i32 count = (eh->all_count[j][i] >> 5) + 1;
- for (i32 c = 0; c < count * 2; c++) printf("\xe2\x96\x88");
-
- printf("\e[0m");
- }
- printf("\n");
- }
-}
-#endif
-
-static void dump_cycles() {
- context.cycle_detected = 1;
- Entity* ent;
-
- while (1) {
- ent = entity_heap_top(&context.entities);
- entity_heap_remove_top(&context.entities);
- if (ent->state < Entity_State_Code_Gen) process_entity(ent);
- else break;
-
- if (bh_arr_length(context.entities.entities) == 0) {
- break;
- }
- }
-}
-
-static i32 onyx_compile() {
- u64 start_time = bh_time_curr();
-
- if (context.options->fun_output)
- printf("\e[2J");
-
- while (!bh_arr_is_empty(context.entities.entities)) {
- Entity* ent = entity_heap_top(&context.entities);
-
-#if defined(_BH_LINUX)
- if (context.options->fun_output) {
- output_dummy_progress_bar();
-
- if (ent->expr->token) {
- OnyxFilePos pos = ent->expr->token->pos;
- printf("\e[0K%s on %s in %s:%d:%d\n", entity_state_strings[ent->state], entity_type_strings[ent->type], pos.filename, pos.line, pos.column);
- }
-
- // Slowing things down for the effect
- usleep(1000);
- }
-#endif
-
- entity_heap_remove_top(&context.entities);
- b32 changed = process_entity(ent);
-
- // NOTE: VERY VERY dumb cycle breaking. Basically, remember the first entity that did
- // not change (i.e. did not make any progress). Then everytime an entity doesn't change,
- // check if it is the same entity. If it is, it means all other entities that were processed
- // between the two occurences didn't make any progress either, and there must be a cycle.
- // - brendanfh 2021/02/06
- //
- // Because of the recent changes to the compiler architecture (again), this condition
- // does not always hold anymore. There can be nodes that get scheduled multiple times
- // before the "key" node that will unblock the progress. This means a more sophisticated
- // cycle detection algorithm must be used.
- //
- static Entity* watermarked_node = NULL;
- static u32 highest_watermark = 0;
- if (!changed) {
- if (!watermarked_node) {
- watermarked_node = ent;
- highest_watermark = bh_max(highest_watermark, ent->macro_attempts);
- }
- else if (watermarked_node == ent) {
- if (ent->macro_attempts > highest_watermark) {
- entity_heap_insert_existing(&context.entities, ent);
-
- if (context.cycle_almost_detected) {
- dump_cycles();
- } else {
- context.cycle_almost_detected = 1;
- }
- }
- }
- else if (watermarked_node->macro_attempts < ent->macro_attempts) {
- watermarked_node = ent;
- highest_watermark = bh_max(highest_watermark, ent->macro_attempts);
- }
- } else {
- watermarked_node = NULL;
- context.cycle_almost_detected = 0;
- }
-
- if (onyx_has_errors()) return ONYX_COMPILER_PROGRESS_ERROR;
-
- if (ent->state != Entity_State_Finalized && ent->state != Entity_State_Failed)
- entity_heap_insert_existing(&context.entities, ent);
- }
-
- u64 duration = bh_time_duration(start_time);
-
- if (context.options->verbose_output > 0) {
- // TODO: Replace these with bh_printf when padded formatting is added.
- printf("\nStatistics:\n");
- printf(" Time taken: %lf seconds\n", (double) duration / 1000);
- printf(" Processed %ld lines (%f lines/second).\n", lexer_lines_processed, ((f32) 1000 * lexer_lines_processed) / (duration));
- printf(" Processed %ld tokens (%f tokens/second).\n", lexer_tokens_processed, ((f32) 1000 * lexer_tokens_processed) / (duration));
- printf("\n");
- }
-
- return ONYX_COMPILER_PROGRESS_SUCCESS;
-}
-
-static void link_wasm_module() {
- Package *runtime_var_package = package_lookup("runtime.vars");
- assert(runtime_var_package);
-
- AstTyped *link_options_node = (AstTyped *) symbol_raw_resolve(runtime_var_package->scope, "link_options");
- Type *link_options_type = type_build_from_ast(context.ast_alloc, builtin_link_options_type);
-
- assert(unify_node_and_type(&link_options_node, link_options_type) == TYPE_MATCH_SUCCESS);
-
- OnyxWasmLinkOptions link_opts;
- // CLEANUP: Properly handle this case.
- assert(onyx_wasm_build_link_options_from_node(&link_opts, link_options_node));
-
- onyx_wasm_module_link(context.wasm_module, &link_opts);
-}
-
-static CompilerProgress onyx_flush_module() {
- link_wasm_module();
-
- // NOTE: Output to file
- bh_file output_file;
- if (bh_file_create(&output_file, context.options->target_file) != BH_FILE_ERROR_NONE)
- return ONYX_COMPILER_PROGRESS_FAILED_OUTPUT;
-
- if (context.options->verbose_output)
- bh_printf("Outputting to WASM file: %s\n", output_file.filename);
-
- // APPARENTLY... the WebAssembly Threading proposal says that the data segment initializations
- // in a WASM module are copied into the linear memory EVERY time the module is instantiated, not
- // just the first time. This means that if we are happily chugging along and modifying global state
- // and then we spawn a thread, that thread will completely wipe all changes to the global and return
- // it to its original state. This is horrible obviously, but the only thing that is more horrible is
- // that the best way around this is to create a second WASM module that simply initializes the given
- // data section. Then have a section module that is actually your code. For right now, this is going
- // to be fine since the browser is really the only place that multi-threading can be used to any
- // degree of competency. But still... This is god awful and I hope that there is some other way to
- // around this down the line.
- if (context.options->use_multi_threading && !context.options->use_post_mvp_features) {
- bh_file data_file;
- if (bh_file_create(&data_file, bh_aprintf(global_scratch_allocator, "%s.data", context.options->target_file)) != BH_FILE_ERROR_NONE)
- return ONYX_COMPILER_PROGRESS_FAILED_OUTPUT;
-
- OnyxWasmModule* data_module = bh_alloc_item(global_heap_allocator, OnyxWasmModule);
- *data_module = onyx_wasm_module_create(global_heap_allocator);
-
- data_module->data = context.wasm_module->data;
- context.wasm_module->data = NULL;
-
- onyx_wasm_module_write_to_file(data_module, data_file);
- onyx_wasm_module_write_to_file(context.wasm_module, output_file);
-
- bh_file_close(&data_file);
- } else {
- onyx_wasm_module_write_to_file(context.wasm_module, output_file);
- }
-
- bh_file_close(&output_file);
-
- if (context.options->documentation_file != NULL) {
- OnyxDocumentation docs = onyx_docs_generate();
- docs.format = Doc_Format_Human;
- onyx_docs_emit(&docs, context.options->documentation_file);
- }
-
- return ONYX_COMPILER_PROGRESS_SUCCESS;
-}
-
-#ifdef ENABLE_RUN_WITH_WASMER
-static b32 onyx_run() {
- link_wasm_module();
-
- bh_buffer code_buffer;
- onyx_wasm_module_write_to_buffer(context.wasm_module, &code_buffer);
-
- onyx_run_initialize(context.options->debug_enabled);
-
- if (context.options->verbose_output > 0)
- bh_printf("Running program:\n");
-
- return onyx_run_wasm(code_buffer, context.options->passthrough_argument_count, context.options->passthrough_argument_data);
-}
-#endif
-
-int main(int argc, char *argv[]) {
-
- bh_scratch_init(&global_scratch, bh_heap_allocator(), 256 * 1024); // NOTE: 256 KiB
- global_scratch_allocator = bh_scratch_allocator(&global_scratch);
-
- // SPEED: This used to be a managed heap allocator where all allocations
- // were tracked and would be automatically freed at the end of execution.
- // I don't know why I ever did that because that is the job of the operating
- // system when a process exits.
- global_heap_allocator = bh_heap_allocator();
-
- CompileOptions compile_opts = compile_opts_parse(global_heap_allocator, argc, argv);
- context_init(&compile_opts);
-
- CompilerProgress compiler_progress = ONYX_COMPILER_PROGRESS_ERROR;
- switch (compile_opts.action) {
- case ONYX_COMPILE_ACTION_PRINT_HELP: bh_printf(docstring); return 1;
-
- case ONYX_COMPILE_ACTION_COMPILE:
- compiler_progress = onyx_compile();
- if (compiler_progress == ONYX_COMPILER_PROGRESS_SUCCESS) {
- onyx_flush_module();
- }
- break;
-
- #ifdef ENABLE_RUN_WITH_WASMER
- case ONYX_COMPILE_ACTION_RUN:
- compiler_progress = onyx_compile();
- if (compiler_progress == ONYX_COMPILER_PROGRESS_SUCCESS) {
- if (!onyx_run()) {
- compiler_progress = ONYX_COMPILER_PROGRESS_ERROR;
- }
- }
- break;
- #endif
-
- default: break;
- }
-
- switch (compiler_progress) {
- case ONYX_COMPILER_PROGRESS_ERROR:
- onyx_errors_print();
- break;
-
- case ONYX_COMPILER_PROGRESS_FAILED_OUTPUT:
- bh_printf_err("Failed to open file for writing: '%s'\n", compile_opts.target_file);
- break;
-
- case ONYX_COMPILER_PROGRESS_SUCCESS:
- break;
- }
-
- context_free();
-
- bh_scratch_free(&global_scratch);
- // bh_managed_heap_free(&global_heap);
-
- return compiler_progress != ONYX_COMPILER_PROGRESS_SUCCESS;
-}
+++ /dev/null
-
-#define BH_DEFINE
-#include "bh.h"
-
-#define ONYX_LIBRARY_NAME onyx_runtime
-#define ONYX_NO_SHORT_NAMES
-#include "onyx_library.h"
-
-#ifdef _BH_LINUX
- #include <pthread.h>
- #include <signal.h>
- #include <sys/wait.h>
- #include <sys/types.h>
- #include <dlfcn.h>
- #include <dirent.h>
- #include <arpa/inet.h>
- #include <netdb.h>
- #include <netinet/in.h>
- #include <sys/socket.h>
- #include <sys/un.h>
- #include <poll.h>
-#endif
-
-#include "types.h" // For POINTER_SIZE
-
-#define ONYX_FILE_ERROR_NONE 0
-#define ONYX_FILE_ERROR_NOT_FOUND 1
-#define ONYX_FILE_ERROR_EXISTS 2
-#define ONYX_FILE_ERROR_PERMISSION 3
-#define ONYX_FILE_ERROR_BAD_FILE 4
-#define ONYX_FILE_ERROR_BAD_MODE 5
-
-ONYX_DEF(__file_open_impl, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
- char *path_ptr = ONYX_PTR(params->data[0].of.i32);
- int path_len = params->data[1].of.i32;
-
- char path[512] = {0};
- path_len = bh_min(path_len, 511);
- strncpy(path, path_ptr, path_len);
- path[path_len] = 0;
-
- int mode = params->data[2].of.i32;
-
- bh_file_mode bh_mode;
- switch (mode) {
- case 1: bh_mode = BH_FILE_MODE_READ; break;
- case 2: bh_mode = BH_FILE_MODE_WRITE; break;
- case 3: bh_mode = BH_FILE_MODE_APPEND; break;
- }
-
- bh_file file;
- bh_file_error error = bh_file_open_mode(&file, bh_mode, path);
- if (error == BH_FILE_ERROR_INVALID) {
- results->data[0] = WASM_I32_VAL(ONYX_FILE_ERROR_NOT_FOUND);
- return NULL;
- }
-
- *(u64 *) ONYX_PTR(params->data[3].of.i32) = (u64) file.fd;
- results->data[0] = WASM_I32_VAL(ONYX_FILE_ERROR_NONE);
- return NULL;
-}
-
-ONYX_DEF(__file_close, (WASM_I64), (WASM_I32)) {
- i64 fd = params->data[0].of.i64;
-
- bh_file file = { (bh_file_descriptor) fd };
- bh_file_error error = bh_file_close(&file);
- if (error == BH_FILE_ERROR_INVALID) {
- results->data[0] = WASM_I32_VAL(ONYX_FILE_ERROR_NOT_FOUND);
- return NULL;
- }
-
- results->data[0] = WASM_I32_VAL(ONYX_FILE_ERROR_NONE);
- return NULL;
-}
-
-ONYX_DEF(__file_exists, (WASM_I32, WASM_I32), (WASM_I32)) {
- char *path_ptr = ONYX_PTR(params->data[0].of.i32);
- int path_len = params->data[1].of.i32;
-
- char path[512] = {0};
- path_len = bh_min(path_len, 511);
- strncpy(path, path_ptr, path_len);
- path[path_len] = 0;
-
- results->data[0] = WASM_I32_VAL(bh_file_exists(path));
- return NULL;
-}
-
-ONYX_DEF(__file_stat, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
- char *path_ptr = ONYX_PTR(params->data[0].of.i32);
- int path_len = params->data[1].of.i32;
-
- char path[512] = {0};
- path_len = bh_min(path_len, 511);
- strncpy(path, path_ptr, path_len);
- path[path_len] = 0;
-
- results->data[0] = WASM_I32_VAL(bh_file_stat(path, ONYX_PTR(params->data[2].of.i32)));
- return NULL;
-}
-
-ONYX_DEF(__file_remove, (WASM_I32, WASM_I32), (WASM_I32)) {
- char *path_ptr = ONYX_PTR(params->data[0].of.i32);
- int path_len = params->data[1].of.i32;
-
- char path[512] = {0};
- path_len = bh_min(path_len, 511);
- strncpy(path, path_ptr, path_len);
- path[path_len] = 0;
-
- results->data[0] = WASM_I32_VAL(bh_file_remove(path));
- return NULL;
-}
-
-ONYX_DEF(__file_seek, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
- i64 fd = params->data[0].of.i64;
- i32 offset = params->data[1].of.i32;
- i32 whence = params->data[2].of.i32;
-
- bh_file file = { (bh_file_descriptor) fd };
- bh_file_whence bh_whence;
- switch (whence) {
- case 0: bh_whence = BH_FILE_WHENCE_BEGIN; break;
- case 1: bh_whence = BH_FILE_WHENCE_CURRENT; break;
- case 2: bh_whence = BH_FILE_WHENCE_END; break;
- }
-
- i64 new_offset = bh_file_seek(&file, offset, whence);
- results->data[0] = WASM_I32_VAL((i32) new_offset);
- return NULL;
-}
-
-ONYX_DEF(__file_tell, (WASM_I64), (WASM_I32)) {
- i64 fd = params->data[0].of.i64;
- bh_file file = { (bh_file_descriptor) fd };
- results->data[0] = WASM_I32_VAL(bh_file_tell(&file));
- return NULL;
-}
-
-ONYX_DEF(__file_read, (WASM_I64, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
- i64 fd = params->data[0].of.i64;
- bh_file file = { (bh_file_descriptor) fd };
-
- i32 curr_pos = bh_file_tell(&file);
- b32 success = bh_file_read_at(&file,
- bh_file_tell(&file),
- ONYX_PTR(params->data[1].of.i32),
- params->data[2].of.i32,
- (i64 *) ONYX_PTR(params->data[3].of.i32));
-
- bh_file_seek_to(&file, curr_pos + *(i32 *) ONYX_PTR(params->data[3].of.i32));
-
- results->data[0] = WASM_I32_VAL(0);
- if (!success) results->data[0] = WASM_I32_VAL(2);
- return NULL;
-}
-
-ONYX_DEF(__file_write, (WASM_I64, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
- i64 fd = params->data[0].of.i64;
- bh_file file = { (bh_file_descriptor) fd };
-
- i32 curr_pos = bh_file_tell(&file);
- b32 success = bh_file_write_at(&file,
- bh_file_tell(&file),
- ONYX_PTR(params->data[1].of.i32),
- params->data[2].of.i32,
- (i64 *) ONYX_PTR(params->data[3].of.i32));
-
- bh_file_seek_to(&file, curr_pos + *(i32 *) ONYX_PTR(params->data[3].of.i32));
-
- results->data[0] = WASM_I32_VAL(0);
- if (!success) results->data[0] = WASM_I32_VAL(2);
- return NULL;
-}
-
-ONYX_DEF(__file_flush, (WASM_I64), (WASM_I32)) {
- i64 fd = params->data[0].of.i64;
- bh_file file = { (bh_file_descriptor) fd };
- bh_file_flush(&file);
- results->data[0] = WASM_I32_VAL(0);
- return NULL;
-}
-
-ONYX_DEF(__file_size, (WASM_I64), (WASM_I32)) {
- i64 fd = params->data[0].of.i64;
- bh_file file = { (bh_file_descriptor) fd };
- results->data[0] = WASM_I32_VAL(bh_file_size(&file));
- return NULL;
-}
-
-ONYX_DEF(__file_get_standard, (WASM_I32, WASM_I32), (WASM_I32)) {
- bh_file_standard standard = (bh_file_standard) params->data[0].of.i32;
-
- bh_file file;
- bh_file_error error = bh_file_get_standard(&file, standard);
- if (error == BH_FILE_ERROR_NONE) {
- *(u64 *) ONYX_PTR(params->data[1].of.i32) = (u64) file.fd;
- }
-
- results->data[0] = WASM_I32_VAL(error == BH_FILE_ERROR_NONE);
- return NULL;
-}
-
-ONYX_DEF(__file_rename, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
- char *old_path_ptr = ONYX_PTR(params->data[0].of.i32);
- int old_path_len = params->data[1].of.i32;
-
- char old_path[512] = {0};
- old_path_len = bh_min(old_path_len, 511);
- strncpy(old_path, old_path_ptr, old_path_len);
- old_path[old_path_len] = 0;
-
- char *new_path_ptr = ONYX_PTR(params->data[2].of.i32);
- int new_path_len = params->data[3].of.i32;
-
- char new_path[512] = {0};
- new_path_len = bh_min(new_path_len, 511);
- strncpy(new_path, new_path_ptr, new_path_len);
- new_path[new_path_len] = 0;
-
-#ifdef _BH_WINDOWS
- results->data[0] = WASM_I32_VAL(MoveFileA(old_path, new_path));
- return NULL;
-#endif
-
-#ifdef _BH_LINUX
- results->data[0] = WASM_I32_VAL(rename(old_path, new_path) == 0);
- return NULL;
-#endif
-}
-
-//
-// Directories
-//
-
-ONYX_DEF(__dir_open, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
- char *path_ptr = ONYX_PTR(params->data[0].of.i32);
- int path_len = params->data[1].of.i32;
-
- char path[512] = {0};
- path_len = bh_min(path_len, 511);
- strncpy(path, path_ptr, path_len);
- path[path_len] = 0;
-
-#ifdef _BH_WINDOWS
- for (int i=0; i<path_len; i++) if (path[i] == '/') path[i] = '\\';
- strncat(path, "\\*.*", 511);
-
- Windows_Directory_Opened* dir = malloc(sizeof(Windows_Directory_Opened));
- dir->hndl = FindFirstFileA(path, &dir->found_file);
- if (dir->hndl == INVALID_HANDLE_VALUE) {
- results->data[0] = WASM_I32_VAL(0);
- return NULL;
- }
-
- *(u64 *) ONYX_PTR(params->data[2].of.i32) = (u64) dir;
-
- results->data[0] = WASM_I32_VAL(1);
- return NULL;
-#endif
-
-#ifdef _BH_LINUX
- DIR* dir = opendir(path);
- *(u64 *) ONYX_PTR(params->data[2].of.i32) = (u64) dir;
- results->data[0] = WASM_I32_VAL(dir != NULL);
- return NULL;
-#endif
-}
-
-// (DIR*, PTR<DIRENT>) -> BOOL
-ONYX_DEF(__dir_read, (WASM_I64, WASM_I32), (WASM_I32)) {
-#ifdef _BH_WINDOWS
- Windows_Directory_Opened* dir = (Windows_Directory_Opened *) params->data[0].of.i64;
- if (dir == NULL) {
- results->data[0] = WASM_I32_VAL(0);
- return NULL;
- }
-
- do {
- BOOL success = FindNextFileA(dir->hndl, &dir->found_file);
- if (!success) {
- results->data[0] = WASM_I32_VAL(0);
- return NULL;
- }
- } while (!strcmp(dir->found_file.cFileName, ".") || !strcmp(dir->found_file.cFileName, ".."));
-
- u32 out = params->data[1].of.i32;
- assert(out != 0);
-
- *(u32 *) ONYX_PTR(out + 0) = (dir->found_file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 3 : 4;
- *(u32 *) ONYX_PTR(out + 4) = 0;
- *(u32 *) ONYX_PTR(out + 8) = strlen(dir->found_file.cFileName);
- strncpy(ONYX_PTR(out + 12), dir->found_file.cFileName, 256);
-
- results->data[0] = WASM_I32_VAL(1);
- return NULL;
-#endif
-
-#ifdef _BH_LINUX
- DIR* dir = (DIR *) params->data[0].of.i64;
- if (dir == NULL) {
- results->data[0] = WASM_I32_VAL(0);
- return NULL;
- }
-
- struct dirent *ent;
- while (1) {
- ent = readdir(dir);
- if (ent == NULL) {
- results->data[0] = WASM_I32_VAL(0);
- return NULL;
- }
-
- // Skip the current directory and parent directory
- if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) break;
- }
-
- u32 type = 0;
- switch (ent->d_type) {
- case DT_UNKNOWN: break;
- case DT_BLK: type = 1; break;
- case DT_CHR: type = 2; break;
- case DT_DIR: type = 3; break;
- case DT_LNK: type = 5; break;
- case DT_REG: type = 4; break;
- default: type = 6; break;
- }
-
- u32 out = params->data[1].of.i32;
- assert(out != 0);
-
- *(u32 *) ONYX_PTR(out + 0) = type;
- *(u32 *) ONYX_PTR(out + 4) = (u32) ent->d_ino;
- *(u32 *) ONYX_PTR(out + 8) = strlen(ent->d_name);
- strncpy(ONYX_PTR(out + 12), ent->d_name, 256);
-
- results->data[0] = WASM_I32_VAL(1);
- return NULL;
-#endif
-}
-
-ONYX_DEF(__dir_close, (WASM_I64), ()) {
-#ifdef _BH_WINDOWS
- Windows_Directory_Opened* dir = (Windows_Directory_Opened *) params->data[0].of.i64;
-
- FindClose(dir->hndl);
- free(dir);
-
- return NULL;
-#endif
-
-#ifdef _BH_LINUX
- DIR* dir = (DIR *) params->data[0].of.i64;
- if (dir == NULL) return NULL;
-
- closedir(dir);
-#endif
- return NULL;
-}
-
-ONYX_DEF(__dir_create, (WASM_I32, WASM_I32), (WASM_I32)) {
- char *path_ptr = ONYX_PTR(params->data[0].of.i32);
- int path_len = params->data[1].of.i32;
-
- char path[512] = {0};
- path_len = bh_min(path_len, 511);
- strncpy(path, path_ptr, path_len);
- path[path_len] = 0;
-
-#ifdef _BH_WINDOWS
- results->data[0] = WASM_I32_VAL(CreateDirectoryA(path));
- return NULL;
-#endif
-
-#ifdef _BH_LINUX
- results->data[0] = WASM_I32_VAL(mkdir(path, 0777) == 0);
- return NULL;
-#endif
-}
-
-ONYX_DEF(__dir_remove, (WASM_I32, WASM_I32), (WASM_I32)) {
- char *path_ptr = ONYX_PTR(params->data[0].of.i32);
- int path_len = params->data[1].of.i32;
-
- char path[512] = {0};
- path_len = bh_min(path_len, 511);
- strncpy(path, path_ptr, path_len);
- path[path_len] = 0;
-
-#ifdef _BH_WINDOWS
- results->data[0] = WASM_I32_VAL(RemoveDirectoryA(path));
- return NULL;
-#endif
-
-#ifdef _BH_LINUX
- results->data[0] = WASM_I32_VAL(rmdir(path) == 0);
- return NULL;
-#endif
-}
-
-//
-// THREADS
-//
-typedef struct OnyxThread {
- i32 id;
- i32 tls_base;
- i32 funcidx;
- i32 dataptr;
- wasm_instance_t* instance;
-
- #ifdef _BH_LINUX
- pthread_t thread;
- #endif
-
- #ifdef _BH_WINDOWS
- HANDLE thread_handle;
- i32 thread_id;
- #endif
-} OnyxThread;
-
-static bh_arr(OnyxThread) threads = NULL;
-
-#ifdef _BH_LINUX
-static void *onyx_run_thread(void *data) {
-#endif
-#ifdef _BH_WINDOWS
-static i32 onyx_run_thread(void *data) {
-#endif
- OnyxThread *thread = (OnyxThread *) data;
-
- wasm_store_t *wasm_store = runtime->wasm_store_new(runtime->wasm_engine);
-
- wasm_trap_t* traps = NULL;
- thread->instance = runtime->wasm_instance_new(wasm_store, runtime->wasm_module, &runtime->wasm_imports, &traps);
-
- wasm_extern_t* start_extern = runtime->wasm_extern_lookup_by_name(runtime->wasm_module, thread->instance, "_thread_start");
- wasm_func_t* start_func = runtime->wasm_extern_as_func(start_extern);
-
- wasm_extern_t* exit_extern = runtime->wasm_extern_lookup_by_name(runtime->wasm_module, thread->instance, "_thread_exit");
- wasm_func_t* exit_func = runtime->wasm_extern_as_func(exit_extern);
-
- wasm_trap_t* trap=NULL;
-
- // NOTE: This is cached in a local variable because there is a tiny chance that if a lot of threads are created
- // then the backing array for the thread handles will move and thread* we have well not be valid. I'm betting on this
- // not happening before now in the function; however, it *could* happen before we call thread_exit. This would be bad
- // because of the normal reasons why accessing memory that you don't own any more is bad.
- i32 thread_id = thread->id;
-
- { // Call the _thread_start procedure
- wasm_val_t args[] = { WASM_I32_VAL(thread_id), WASM_I32_VAL(thread->tls_base), WASM_I32_VAL(thread->funcidx), WASM_I32_VAL(thread->dataptr) };
- wasm_val_vec_t results = { 0, 0 };
- wasm_val_vec_t args_array = WASM_ARRAY_VEC(args);
-
- trap = runtime->wasm_func_call(start_func, &args_array, &results);
- if (trap != NULL) {
- bh_printf("THREAD: %d\n", thread_id);
- runtime->onyx_print_trap(trap);
- }
- }
-
- { // Call the _thread_exit procedure
- wasm_val_t args[] = { WASM_I32_VAL(thread_id) };
- wasm_val_vec_t results = { 0, 0 };
- wasm_val_vec_t args_array = WASM_ARRAY_VEC(args);
-
- trap = runtime->wasm_func_call(exit_func, &args_array, &results);
- }
-
- runtime->wasm_store_delete(wasm_store);
-
- return 0;
-}
-
-ONYX_DEF(__spawn_thread, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
- if (threads == NULL) bh_arr_new(bh_heap_allocator(), threads, 128);
- bh_arr_insert_end(threads, 1);
- OnyxThread *thread = &bh_arr_last(threads);
-
- thread->id = params->data[0].of.i32;
- thread->tls_base = params->data[1].of.i32;
- thread->funcidx = params->data[2].of.i32;
- thread->dataptr = params->data[3].of.i32;
-
- #ifdef _BH_LINUX
- pthread_create(&thread->thread, NULL, onyx_run_thread, thread);
- #endif
-
- #ifdef _BH_WINDOWS
- // thread->thread_handle = CreateThread(NULL, 0, onyx_run_thread, thread, 0, &thread->thread_id);
- thread->thread_handle = (HANDLE) _beginthreadex(NULL, 0, onyx_run_thread, thread, 0, &thread->thread_id);
- #endif
-
- results->data[0] = WASM_I32_VAL(1);
- return NULL;
-}
-
-ONYX_DEF(__kill_thread, (WASM_I32), (WASM_I32)) {
- i32 thread_id = params->data[0].of.i32;
-
- i32 i = 0;
- bh_arr_each(OnyxThread, thread, threads) {
- if (thread->id == thread_id) {
- #ifdef _BH_LINUX
- // This leads to some weirdness and bugs...
- //
- pthread_kill(thread->thread, SIGKILL);
- #endif
-
- #ifdef _BH_WINDOWS
- TerminateThread(thread->thread_handle, 0);
- CloseHandle(thread->thread_handle);
- #endif
-
- bh_arr_deleten(threads, i, 1);
- results->data[0] = WASM_I32_VAL(1);
- return NULL;
- }
-
- i++;
- }
-
- results->data[0] = WASM_I32_VAL(0);
- return NULL;
-}
-
-
-
-
-//
-// PROCESS
-//
-#define ONYX_PROCESS_MAGIC_NUMBER 0xdeadfadebabecafe
-typedef struct OnyxProcess {
- u64 magic_number;
-
-#ifdef _BH_LINUX
- // Pipes
- i32 proc_to_host[2];
- i32 host_to_proc[2];
-
- pid_t pid;
-#endif
-
-#ifdef _BH_WINDOWS
- HANDLE proc_to_host_read;
- HANDLE proc_to_host_write;
- HANDLE host_to_proc_read;
- HANDLE host_to_proc_write;
-
- PROCESS_INFORMATION proc_info;
-#endif
-} OnyxProcess;
-
-ONYX_DEF(__process_spawn, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I64)) {
- char* process_str = ONYX_PTR(params->data[0].of.i32);
- i32 process_len = params->data[1].of.i32;
- i32 args_ptr = params->data[2].of.i32;
- i32 args_len = params->data[3].of.i32;
- b32 blocking_io = !params->data[4].of.i32;
- char *cwd_str = ONYX_PTR(params->data[5].of.i32);
- i32 cwd_len = params->data[6].of.i32;
-
- char process_path[1024];
- process_len = bh_min(1023, process_len);
- memcpy(process_path, process_str, process_len);
- process_path[process_len] = '\0';
-
- char starting_dir[1024];
- if (cwd_len > 0) {
- cwd_len = bh_min(1023, cwd_len);
- memcpy(starting_dir, cwd_str, cwd_len);
- starting_dir[cwd_len] = '\0';
- }
-
- OnyxProcess *process = malloc(sizeof(OnyxProcess));
- memset(process, 0, sizeof(*process));
- process->magic_number = ONYX_PROCESS_MAGIC_NUMBER;
-
- #ifdef _BH_LINUX
- // :Security - alloca a user controlled string.
- char **process_args = alloca(sizeof(char *) * (args_len + 2));
- byte_t* array_loc = ONYX_PTR(args_ptr);
- fori (i, 0, args_len) {
- char *arg_str = ONYX_PTR(*(i32 *) (array_loc + i * 2 * POINTER_SIZE));
- i32 arg_len = *(i32 *) (array_loc + i * 2 * POINTER_SIZE + POINTER_SIZE);
-
- char *arg = alloca(sizeof(char) * (arg_len + 1));
- memcpy(arg, arg_str, arg_len);
- arg[arg_len] = '\0';
- process_args[i + 1] = arg;
- }
- process_args[0] = process_path;
- process_args[args_len + 1] = NULL;
-
- if (pipe(process->proc_to_host) || pipe(process->host_to_proc)) {
- wasm_val_init_ptr(&results->data[0], NULL); // Failed to run
- return NULL;
- }
-
- pid_t pid;
- switch (pid = fork()) {
- case -1: // Bad fork
- wasm_val_init_ptr(&results->data[0], NULL); // Failed to run
-
- close(process->proc_to_host[0]);
- close(process->proc_to_host[1]);
- close(process->host_to_proc[0]);
- close(process->host_to_proc[1]);
- break;
-
- case 0: // Child process
- close(process->proc_to_host[0]);
- close(process->host_to_proc[1]);
- dup2(process->host_to_proc[0], 0); // Map the output to the pipe
- dup2(process->proc_to_host[1], 1); // Map the output to the pipe
- dup2(process->proc_to_host[1], 2); // Stderr to stdout
-
- if (!blocking_io) {
- fcntl(0, F_SETFL, O_NONBLOCK);
- fcntl(1, F_SETFL, O_NONBLOCK);
- }
-
- if (cwd_len > 0) {
- chdir(starting_dir); // Switch current working directory.
- }
-
- execvp(process_path, process_args);
- exit(-1);
- break;
-
- default: {
- process->pid = pid;
- close(process->host_to_proc[0]);
- close(process->proc_to_host[1]);
-
- wasm_val_init_ptr(&results->data[0], process);
- break;
- }
- }
-
- #endif
-
- #ifdef _BH_WINDOWS
- // CLEANUP CLEANUP CLEANUP: This is so freaking bad...
- char cmdLine[2048];
- memset(cmdLine, 0, 2048);
- strncat(cmdLine, "\"", 2047);
- strncat(cmdLine, process_path, 2047);
- strncat(cmdLine, "\"", 2047);
-
- byte_t* array_loc = ONYX_PTR(args_ptr);
- fori (i, 0, args_len) {
- char *arg_str = ONYX_PTR(*(i32 *) (array_loc + i * 2 * POINTER_SIZE));
- i32 arg_len = *(i32 *) (array_loc + i * 2 * POINTER_SIZE + 4);
-
- strncat(cmdLine, " \"", 2047);
- strncat(cmdLine, arg_str, arg_len);
- strncat(cmdLine, "\"", 2047);
- }
-
- STARTUPINFOA startup;
- memset(&startup, 0, sizeof startup);
- startup.cb = sizeof(startup);
-
- SECURITY_ATTRIBUTES saAttr;
- saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
- saAttr.lpSecurityDescriptor = NULL;
- saAttr.bInheritHandle = 1;
-
- i32 success = 1;
- success = success && CreatePipe(&process->host_to_proc_read, &process->host_to_proc_write, &saAttr, 4096);
- success = success && CreatePipe(&process->proc_to_host_read, &process->proc_to_host_write, &saAttr, 4096);
- if (!success) {
- // printf("FAILED TO CREATE PIPES: %d\n", GetLastError());
- wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
- return NULL;
- }
-
- success = SetHandleInformation(process->proc_to_host_read, 1 /* HANDLE_FLAG_INHERIT */, 0);
- success = success && SetHandleInformation(process->host_to_proc_write, 1 /* HANDLE_FLAG_INHERIT */, 0);
- if (!success) {
- // printf("FAILED TO CONFIGURE PIPES: %d\n", GetLastError());
- wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
- return NULL;
- }
-
- startup.hStdInput = process->host_to_proc_read;
- startup.hStdOutput = process->proc_to_host_write;
- startup.hStdError = process->proc_to_host_write;
-
- startup.dwFlags |= STARTF_USESTDHANDLES;
-
- memset(&process->proc_info, 0, sizeof process->proc_info);
-
- char *working_dir = NULL;
- if (cwd_len > 0) {
- working_dir = starting_dir;
- }
-
- success = CreateProcessA(process_path, cmdLine, &saAttr, &saAttr, 1, 0, NULL, working_dir, &startup, &process->proc_info);
- if (!success) {
- printf("FAILED TO CREATE PROCESS: %d\n", GetLastError());
- wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
- return NULL;
- }
-
- CloseHandle(process->proc_to_host_write);
- CloseHandle(process->host_to_proc_read);
- wasm_val_init_ptr(&results->data[0], process);
- #endif
-
- return NULL;
-}
-
-ONYX_DEF(__process_read, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
- OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
- if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
- results->data[0] = WASM_I32_VAL(0);
- return NULL;
- }
-
- i32 output_ptr = params->data[1].of.i32;
- i32 output_len = params->data[2].of.i32;
- u8 *buffer = ONYX_PTR(output_ptr);
-
- i32 bytes_read;
- #ifdef _BH_LINUX
- bytes_read = read(process->proc_to_host[0], buffer, output_len);
- if (bytes_read < 0) {
- switch (errno) {
- case EAGAIN: bytes_read = 0; break;
- case EBADF: bytes_read = -1; break;
- default: bytes_read = -2; break;
- }
- }
- #endif
-
- #ifdef _BH_WINDOWS
- i32 success = ReadFile(process->proc_to_host_read, buffer, output_len, &bytes_read, NULL);
- if (!success) {
- bytes_read = -1;
- }
- #endif
-
- results->data[0] = WASM_I32_VAL(bytes_read);
- return NULL;
-}
-
-ONYX_DEF(__process_write, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
- OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
- if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
- results->data[0] = WASM_I32_VAL(0);
- return NULL;
- }
-
- i32 input_ptr = params->data[1].of.i32;
- i32 input_len = params->data[2].of.i32;
- u8 *buffer = ONYX_PTR(input_ptr);
-
- i32 bytes_written;
- #ifdef _BH_LINUX
- bytes_written = write(process->host_to_proc[1], buffer, input_len);
- bytes_written = bh_max(bytes_written, 0); // Silently consume errors
- #endif
-
- #ifdef _BH_WINDOWS
- i32 success = WriteFile(process->host_to_proc_write, buffer, input_len, &bytes_written, NULL);
- if (!success) bytes_written = 0;
- #endif
-
- results->data[0] = WASM_I32_VAL(bytes_written);
- return NULL;
-}
-
-ONYX_DEF(__process_kill, (WASM_I64), (WASM_I32)) {
- OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
- if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
- results->data[0] = WASM_I32_VAL(0);
- return NULL;
- }
-
- #ifdef _BH_LINUX
- i32 failed = kill(process->pid, SIGKILL);
- results->data[0] = WASM_I32_VAL(!failed);
- #endif
-
- #ifdef _BH_WINDOWS
- i32 success = TerminateProcess(process->proc_info.hProcess, 1);
- results->data[0] = WASM_I32_VAL(success ? 1 : 0);
- #endif
-
- return NULL;
-}
-
-ONYX_DEF(__process_wait, (WASM_I64), (WASM_I32)) {
- OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
- if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
- results->data[0] = WASM_I32_VAL(1);
- return NULL;
- }
-
- #ifdef _BH_LINUX
- i32 status;
- waitpid(process->pid, &status, 0);
-
- i32 exit_code = WEXITSTATUS(status);
- results->data[0] = WASM_I32_VAL(exit_code != 0 ? 2 : 0);
- #endif
-
- #ifdef _BH_WINDOWS
- DWORD exitCode;
- while (1) {
- if (!WaitForSingleObject(process->proc_info.hProcess, INFINITE)) {
- // HACK HACK HACK
- DWORD error = GetLastError();
- if (error != 109 && error != 6) {
- // printf("ERROR IN WAIT FOR SINGLE: %d\n", error);
- results->data[0] = WASM_I32_VAL(1);
- return NULL;
- }
- }
-
- if (!GetExitCodeProcess(process->proc_info.hProcess, &exitCode)) {
- // HACK HACK HACK
- // Apparently, I'm doing something wrong (maybe?) where the process handle becomes
- // invalid and causes error 6 "invalid handle". So I think I can safely assume that
- // if that is the case, then the process exited? probably successfuly? hopefully?
- // Honestly I don't know and I can't find any documentation describing when a process
- // handle goes invalid, other than after you close it explicitly. But in the run_tests
- // script, I'm not calling either process_kill or process_destroy, which are the only
- // other functions that close the process handle. So I'm left in the dark as to why this
- // is happening, but oh well. This works for now.
- // - brendanfh 2021/12/03
- if (GetLastError() == 6) {
- exitCode = 0;
- break;
- }
-
- results->data[0] = WASM_I32_VAL(3);
- return NULL;
- }
-
- // 259 is STILL_ACTIVE (aka STATUS_PENDING), which means that the process has not yet exited
- if (exitCode != 259) break;
- }
-
- results->data[0] = WASM_I32_VAL(exitCode != 0 ? 2 : 0);
- #endif
-
- return NULL;
-}
-
-ONYX_DEF(__process_destroy, (WASM_I64), ()) {
- OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
- if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
- return NULL;
- }
-
- #ifdef _BH_LINUX
- close(process->proc_to_host[0]);
- close(process->host_to_proc[1]);
- #endif
-
- #ifdef _BH_WINDOWS
- if (!CloseHandle(process->proc_info.hThread)
- || !CloseHandle(process->proc_info.hProcess)) {
- // printf("ERROR CLOSING HANDLES: %d\n", GetLastError());
- }
- #endif
-
- free(process);
-
- return NULL;
-}
-
-ONYX_DEF(__args_get, (WASM_I32, WASM_I32), ()) {
- if (runtime->argc == 0 || runtime->argv == NULL) {
- return NULL;
- }
-
- i32 buffer_base = params->data[1].of.i32;
-
- for (int i=0; i<runtime->argc; i++) {
- // Should this be strncpy? What would the length be?
- strcpy(ONYX_PTR(buffer_base), runtime->argv[i]);
- *(i32 *) ONYX_PTR(params->data[0].of.i32 + i * 4) = buffer_base;
- buffer_base += strlen(runtime->argv[i]) + 1;
- }
-
- return NULL;
-}
-
-ONYX_DEF(__args_sizes_get, (WASM_I32, WASM_I32), ()) {
- *(i32 *) ONYX_PTR(params->data[0].of.i32) = runtime->argc;
-
- i32 argv_size = 0;
- for (int i=0; i<runtime->argc; i++) {
- argv_size += strlen(runtime->argv[i]) + 1;
- }
-
- *(i32 *) ONYX_PTR(params->data[1].of.i32) = argv_size;
- return NULL;
-}
-
-ONYX_DEF(__exit, (WASM_I32), ()) {
- exit(params->data[0].of.i32);
- return NULL;
-}
-
-ONYX_DEF(__sleep, (WASM_I32), ()) {
- #ifdef _BH_LINUX
- usleep(params->data[0].of.i32 * 1000);
- #endif
-
- #ifdef _BH_WINDOWS
- Sleep(params->data[0].of.i32);
- #endif
- return NULL;
-}
-
-ONYX_DEF(__time, (), (WASM_I64)) {
- results->data[0] = WASM_I64_VAL(bh_time_curr());
- return NULL;
-}
-
-
-
-
-//
-// Dates and Times
-//
-ONYX_DEF(__time_localtime, (WASM_I64, WASM_I32), ()) {
- u64 t = params->data[0].of.i64;
- *(struct tm *) ONYX_PTR(params->data[1].of.i32) = *localtime(&t);
- return NULL;
-}
-
-ONYX_DEF(__time_gmtime, (WASM_I64, WASM_I32), ()) {
- u64 t = params->data[0].of.i64;
- *(struct tm *) ONYX_PTR(params->data[1].of.i32) = *gmtime(&t);
- return NULL;
-}
-
-ONYX_DEF(__time_strftime, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
- u32 len = strftime(ONYX_PTR(params->data[0].of.i32), params->data[1].of.i32, ONYX_PTR(params->data[2].of.i32), ONYX_PTR(params->data[3].of.i32));
- results->data[0] = WASM_I32_VAL(len);
- return NULL;
-}
-
-ONYX_DEF(__time_strptime, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
- #if defined(_BH_LINUX)
- char *rem = strptime(ONYX_PTR(params->data[0].of.i32), params->data[1].of.i32, ONYX_PTR(params->data[2].of.i32), ONYX_PTR(params->data[3].of.i32));
- results->data[0] = WASM_I32_VAL(rem != NULL);
- #else
- results->data[0] = WASM_I32_VAL(0);
- #endif
- return NULL;
-}
-
-
-
-//
-// Networking
-//
-struct onyx_socket_addr {
- unsigned short family;
- unsigned short port;
- unsigned int addr;
-};
-
-static inline int onyx_socket_domain(int i) {
- #if defined(_BH_LINUX)
- switch (i) { // :EnumDependent
- case 0: return AF_UNIX;
- case 1: return AF_INET;
- case 2: return AF_INET6;
- default: return -1;
- }
- #elif defined(_BH_WINDOWS)
- return -1;
- #endif
-}
-
-static inline int onyx_socket_protocol(int i) {
- #if defined(_BH_LINUX)
- switch (i) { // :EnumDependent
- case 0: return SOCK_STREAM;
- case 1: return SOCK_DGRAM;
- default: return -1;
- }
- #elif defined(_BH_WINDOWS)
- return -1;
- #endif
-}
-
-ONYX_DEF(__net_create_socket, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-
- #ifdef _BH_LINUX
- int domain = onyx_socket_domain(params->data[1].of.i32);
- if (domain == -1) goto bad_settings;
-
- int type = onyx_socket_protocol(params->data[2].of.i32);
- if (type == -1) goto bad_settings;
-
- *((int *) ONYX_PTR(params->data[0].of.i32)) = socket(domain, type, 0);
-
- results->data[0] = WASM_I32_VAL(0);
- return NULL;
- #endif
-
- #ifdef _BH_WINDOWS
- #endif
-
-bad_settings:
- results->data[0] = WASM_I32_VAL(1); // :EnumDependent
- return NULL;
-}
-
-ONYX_DEF(__net_close_socket, (WASM_I32), ()) {
- #ifdef _BH_LINUX
- shutdown(params->data[0].of.i32, SHUT_RDWR);
- close(params->data[0].of.i32);
- #endif
-
- #ifdef _BH_WINDOWS
- #endif
-
- return NULL;
-}
-
-ONYX_DEF(__net_setting, (WASM_I32, WASM_I32, WASM_I32), ()) {
- #ifdef _BH_LINUX
- switch (params->data[1].of.i32) {
- case 1: { // :EnumDependent Non-Blocking
- int s = params->data[0].of.i32;
- int flags = fcntl(s, F_GETFL, 0);
- if (params->data[2].of.i32) {
- flags |= O_NONBLOCK;
- } else {
- flags &= ~O_NONBLOCK;
- }
-
- fcntl(s, F_SETFL, flags);
- break;
- }
-
- case 2: { // :EnumDependent Broadcast
- int s = params->data[0].of.i32;
- setsockopt(s, SOL_SOCKET, SO_BROADCAST, (void *) ¶ms->data[2].of.i32, sizeof(int));
- break;
- }
- }
- #endif
-
- return NULL;
-}
-
-ONYX_DEF(__net_bind, (WASM_I32, WASM_I32), (WASM_I32)) {
-
- #ifdef _BH_LINUX
- int res = -1;
-
- struct onyx_socket_addr *oaddr = (void *) ONYX_PTR(params->data[1].of.i32);
- int family = onyx_socket_domain(oaddr->family);
- int port = oaddr->port;
-
- switch (family) {
- case AF_INET: {
- struct sockaddr_in bind_addr;
- memset(&bind_addr, 0, sizeof(bind_addr));
-
- bind_addr.sin_family = AF_INET;
- bind_addr.sin_addr.s_addr = htonl(oaddr->addr);
- bind_addr.sin_port = htons(port);
-
- res = bind(params->data[0].of.i32, &bind_addr, sizeof(bind_addr));
- break;
- }
-
- case AF_INET6: {
- struct sockaddr_in6 bind_addr;
- memset(&bind_addr, 0, sizeof(bind_addr));
-
- bind_addr.sin6_family = AF_INET6;
- memcpy(&bind_addr.sin6_addr.s6_addr, (void *) &oaddr->addr, 16);
- bind_addr.sin6_port = htons(port);
-
- res = bind(params->data[0].of.i32, &bind_addr, sizeof(bind_addr));
- break;
- }
-
- case AF_UNIX: {
- struct sockaddr_un bind_addr;
- memset(&bind_addr, 0, sizeof(bind_addr));
-
- bind_addr.sun_family = AF_UNIX;
- strncpy(&bind_addr.sun_path, (char *) &oaddr->addr, 108);
-
- res = bind(params->data[0].of.i32, &bind_addr, sizeof(bind_addr));
- break;
- }
- }
-
- results->data[0] = WASM_I32_VAL(res >= 0);
- #endif
-
- #ifdef _BH_WINDOWS
- #endif
-
- return NULL;
-}
-
-ONYX_DEF(__net_listen, (WASM_I32, WASM_I32), ()) {
- #ifdef _BH_LINUX
- listen(params->data[0].of.i32, params->data[1].of.i32);
- #endif
-
- #ifdef _BH_WINDOWS
- #endif
-
- return NULL;
-}
-
-ONYX_DEF(__net_accept, (WASM_I32, WASM_I32), (WASM_I32)) {
- #ifdef _BH_LINUX
- struct sockaddr_in client_addr;
- int client_len = sizeof(client_addr);
- memset(&client_addr, 0, client_len);
-
- int client_socket = accept(params->data[0].of.i32, &client_addr, &client_len);
-
- struct onyx_socket_addr* out_addr = (struct onyx_socket_addr *) ONYX_PTR(params->data[1].of.i32);
- out_addr->family = client_addr.sin_family;
- out_addr->port = ntohs(client_addr.sin_port);
- out_addr->addr = ntohl(client_addr.sin_addr.s_addr);
-
- results->data[0] = WASM_I32_VAL(client_socket);
- #endif
-
- return NULL;
-}
-
-ONYX_DEF(__net_connect_unix, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
- #ifdef _BH_LINUX
- int hostlen = params->data[2].of.i32;
- char *hostname = alloca(hostlen + 1);
- memcpy(hostname, ONYX_PTR(params->data[1].of.i32), hostlen);
- hostname[hostlen] = '\0';
-
- struct sockaddr_un server_addr;
- memset(&server_addr, 0, sizeof(server_addr));
-
- server_addr.sun_family = AF_UNIX; // See comment above
- memcpy((char *)&server_addr.sun_path, hostname, hostlen);
-
- int result = connect(params->data[0].of.i32, &server_addr, sizeof(server_addr));
- if (result == 0) results->data[0] = WASM_I32_VAL(0);
- else results->data[0] = WASM_I32_VAL(3); // :EnumDependent
- #endif
-
- return NULL;
-}
-
-ONYX_DEF(__net_connect_ipv4, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
- #ifdef _BH_LINUX
- int hostlen = params->data[2].of.i32;
- char *hostname = alloca(hostlen + 1);
- memcpy(hostname, ONYX_PTR(params->data[1].of.i32), hostlen);
- hostname[hostlen] = '\0';
-
- struct hostent *host;
- host = gethostbyname(hostname); // TODO: Replace this call, as it is obselete.
- if (host == NULL) {
- results->data[0] = WASM_I32_VAL(2); // :EnumDependent
- return NULL;
- }
-
- struct sockaddr_in server_addr;
- memset(&server_addr, 0, sizeof(server_addr));
-
- server_addr.sin_family = AF_INET; // See comment above
- memcpy((char *)&server_addr.sin_addr.s_addr, (char *)host->h_addr, host->h_length);
- server_addr.sin_port = htons(params->data[3].of.i32);
-
- int result = connect(params->data[0].of.i32, &server_addr, sizeof(server_addr));
- if (result == 0) results->data[0] = WASM_I32_VAL(0);
- else results->data[0] = WASM_I32_VAL(3); // :EnumDependent
-
- return NULL;
- #endif
-
- #ifdef _BH_WINDOWS
- #endif
-}
-
-ONYX_DEF(__net_send, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
- #ifdef _BH_LINUX
- // TODO: The flags at the end should be controllable.
- int sent = send(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, MSG_NOSIGNAL);
- results->data[0] = WASM_I32_VAL(sent);
- #endif
-
- return NULL;
-}
-
-ONYX_DEF(__net_sendto, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
- #ifdef _BH_LINUX
- struct sockaddr_in dest_addr;
- int dest_addr_len = sizeof(dest_addr);
- memset(&dest_addr, 0, dest_addr_len);
-
- struct onyx_socket_addr *o_addr = (struct onyx_socket_addr *) ONYX_PTR(params->data[3].of.i32);
- dest_addr.sin_family = AF_INET; // TODO: See other comments related to AF_NET above.
- dest_addr.sin_port = htons(o_addr->port);
- dest_addr.sin_addr.s_addr = htonl(o_addr->addr);
-
- // TODO: The flags at the end should be controllable.
- int sent = sendto(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, MSG_NOSIGNAL, &dest_addr, dest_addr_len);
- results->data[0] = WASM_I32_VAL(sent);
- #endif
-
- return NULL;
-}
-
-ONYX_DEF(__net_recv, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
- *(i32 *) ONYX_PTR(params->data[3].of.i32) = 0;
-
- #ifdef _BH_LINUX
- // TODO: The flags at the end should be controllable.
- int received = recv(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, 0);
- results->data[0] = WASM_I32_VAL(received);
-
- if (received < 0) {
- if (errno == EAGAIN || errno == EWOULDBLOCK) {
- *(i32 *) ONYX_PTR(params->data[3].of.i32) = 1;
- }
- }
- #endif
-
- return NULL;
-}
-
-ONYX_DEF(__net_recvfrom, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
- *(i32 *) ONYX_PTR(params->data[4].of.i32) = 0;
-
- #ifdef _BH_LINUX
- struct onyx_socket_addr *out_addr = (struct onyx_socket_addr *) ONYX_PTR(params->data[3].of.i32);
-
- struct sockaddr_in client_addr;
- int socket_len = sizeof(client_addr);
- memset(&client_addr, 0, socket_len);
-
- int received = recvfrom(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, 0, &client_addr, &socket_len);
- out_addr->family = client_addr.sin_family;
- out_addr->port = ntohs(client_addr.sin_port);
- out_addr->addr = ntohl(client_addr.sin_addr.s_addr);
-
- results->data[0] = WASM_I32_VAL(received);
-
- if (received < 0) {
- if (errno == EAGAIN || errno == EWOULDBLOCK) {
- *(i32 *) ONYX_PTR(params->data[3].of.i32) = 1;
- }
- }
- #endif
-
- return NULL;
-}
-
-ONYX_DEF(__net_poll_recv, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), ()) {
- #ifdef _BH_LINUX
- int i, res, cursor;
- struct pollfd* fds;
-
- fds = alloca(params->data[1].of.i32 * sizeof(struct pollfd));
-
- for (i=0; i<params->data[1].of.i32; i++) {
- fds[i].fd = *(i32 *) ONYX_PTR(params->data[0].of.i32 + 4 * i);
- fds[i].events = POLLIN;
- fds[i].revents = 0;
- }
-
- res = poll(fds, params->data[1].of.i32, params->data[2].of.i32);
-
- for (i=0; i<params->data[1].of.i32; i++) {
- *(i32 *) ONYX_PTR(params->data[3].of.i32 + 4 * i) = 0; // NO_CHANGE
-
- if (fds[i].revents & POLLIN) {
- *(i32 *) ONYX_PTR(params->data[3].of.i32 + 4 * i) = 1; // READABLE
- }
-
- if ((fds[i].revents & POLLHUP)
- || (fds[i].revents & POLLNVAL)
- || (fds[i].revents & POLLERR)) {
- *(i32 *) ONYX_PTR(params->data[3].of.i32 + 4 * i) = 2; // CLOSED
- }
- }
-
- #endif
-
- return NULL;
-}
-
-ONYX_DEF(__net_host_to_net_s, (WASM_I32), (WASM_I32)) {
- results->data[0] = WASM_I32_VAL(htons(params->data[0].of.i32));
- return NULL;
-}
-
-ONYX_DEF(__net_host_to_net_l, (WASM_I32), (WASM_I32)) {
- results->data[0] = WASM_I32_VAL(htonl(params->data[0].of.i32));
- return NULL;
-}
-
-ONYX_DEF(__net_net_to_host_s, (WASM_I32), (WASM_I32)) {
- results->data[0] = WASM_I32_VAL(ntohs(params->data[0].of.i32));
- return NULL;
-}
-
-ONYX_DEF(__net_net_to_host_l, (WASM_I32), (WASM_I32)) {
- results->data[0] = WASM_I32_VAL(ntohl(params->data[0].of.i32));
- return NULL;
-}
-
-
-//
-// C-Pointers
-//
-// These are wildly unsafe and break the core principles of the security
-// of WebAssembly, so there should be a way to turn them off!
-//
-ONYX_DEF(__cptr_make, (WASM_I32), (WASM_I64)) {
- wasm_val_init_ptr(&results->data[0], ONYX_PTR(params->data[0].of.i32));
- return NULL;
-}
-
-ONYX_DEF(__cptr_read, (WASM_I64, WASM_I32, WASM_I32), ()) {
- memcpy(ONYX_PTR(params->data[1].of.i32), (void *) params->data[0].of.i64, params->data[2].of.i32);
- return NULL;
-}
-
-ONYX_DEF(__cptr_read_u8, (WASM_I64), (WASM_I32)) {
- results->data[0] = WASM_I32_VAL(*(u8 *) params->data[0].of.i64);
- return NULL;
-}
-
-ONYX_DEF(__cptr_read_u16, (WASM_I64), (WASM_I32)) {
- results->data[0] = WASM_I32_VAL(*(u16 *) params->data[0].of.i64);
- return NULL;
-}
-
-ONYX_DEF(__cptr_read_u32, (WASM_I64), (WASM_I32)) {
- results->data[0] = WASM_I32_VAL(*(u32 *) params->data[0].of.i64);
- return NULL;
-}
-
-ONYX_DEF(__cptr_read_u64, (WASM_I64), (WASM_I64)) {
- results->data[0] = WASM_I64_VAL(*(u64 *) params->data[0].of.i64);
- return NULL;
-}
-
-ONYX_DEF(__cptr_extract_str, (WASM_I64, WASM_I32, WASM_I32), (WASM_I32)) {
- unsigned int len = strlen((char *) params->data[0].of.i64);
-
- if (params->data[2].of.i32 != 0) {
- strncpy(ONYX_PTR(params->data[1].of.i32), (char *) params->data[0].of.i64, params->data[2].of.i32);
- }
-
- results->data[0] = WASM_I32_VAL(len);
- return NULL;
-}
-
-ONYX_LIBRARY {
- ONYX_FUNC(__file_open_impl)
- ONYX_FUNC(__file_close)
- ONYX_FUNC(__file_exists)
- ONYX_FUNC(__file_stat)
- ONYX_FUNC(__file_remove)
- ONYX_FUNC(__file_seek)
- ONYX_FUNC(__file_tell)
- ONYX_FUNC(__file_read)
- ONYX_FUNC(__file_write)
- ONYX_FUNC(__file_flush)
- ONYX_FUNC(__file_size)
- ONYX_FUNC(__file_get_standard)
- ONYX_FUNC(__file_rename)
-
- ONYX_FUNC(__dir_open)
- ONYX_FUNC(__dir_read)
- ONYX_FUNC(__dir_close)
- ONYX_FUNC(__dir_create)
- ONYX_FUNC(__dir_remove)
-
- ONYX_FUNC(__spawn_thread)
- ONYX_FUNC(__kill_thread)
-
- ONYX_FUNC(__process_spawn)
- ONYX_FUNC(__process_read)
- ONYX_FUNC(__process_write)
- ONYX_FUNC(__process_kill)
- ONYX_FUNC(__process_wait)
- ONYX_FUNC(__process_destroy)
-
- ONYX_FUNC(__args_get)
- ONYX_FUNC(__args_sizes_get)
-
- ONYX_FUNC(__exit)
- ONYX_FUNC(__sleep)
- ONYX_FUNC(__time)
-
- ONYX_FUNC(__time_localtime)
- ONYX_FUNC(__time_gmtime)
- ONYX_FUNC(__time_strftime)
- ONYX_FUNC(__time_strptime)
-
- ONYX_FUNC(__net_create_socket)
- ONYX_FUNC(__net_close_socket)
- ONYX_FUNC(__net_setting)
- ONYX_FUNC(__net_bind)
- ONYX_FUNC(__net_listen)
- ONYX_FUNC(__net_accept)
- ONYX_FUNC(__net_connect_unix)
- ONYX_FUNC(__net_connect_ipv4)
- ONYX_FUNC(__net_send)
- ONYX_FUNC(__net_sendto)
- ONYX_FUNC(__net_recv)
- ONYX_FUNC(__net_recvfrom)
- ONYX_FUNC(__net_poll_recv)
- ONYX_FUNC(__net_host_to_net_s)
- ONYX_FUNC(__net_host_to_net_l)
- ONYX_FUNC(__net_net_to_host_s)
- ONYX_FUNC(__net_net_to_host_l)
-
- ONYX_FUNC(__cptr_make)
- ONYX_FUNC(__cptr_read)
- ONYX_FUNC(__cptr_read_u8)
- ONYX_FUNC(__cptr_read_u16)
- ONYX_FUNC(__cptr_read_u32)
- ONYX_FUNC(__cptr_read_u64)
- ONYX_FUNC(__cptr_extract_str)
-
- NULL
-};
+++ /dev/null
-#define BH_DEFINE
-#define BH_NO_TABLE
-#define STB_DS_IMPLEMENTATION
-#include "bh.h"
-
-#include "wasm_emit.h"
-
-int main(int argc, char *argv[]) {
- i32 wasm_file_idx = 1;
- if (argc < 2) {
- fprintf(stderr, "Expected a WASM file to run.\n");
- return 1;
- }
-
- b32 debug = 0;
- if (!strcmp(argv[1], "--debug")) {
- debug = 1;
- wasm_file_idx = 2;
- }
-
- if (debug && argc < 3) {
- fprintf(stderr, "Expected a WASM file to run.\n");
- return 1;
- }
-
- onyx_run_initialize(debug);
-
- bh_file wasm_file;
- bh_file_error err = bh_file_open(&wasm_file, argv[wasm_file_idx]);
- if (err != BH_FILE_ERROR_NONE) {
- fprintf(stderr, "Failed to open file %s", argv[wasm_file_idx]);
- return 1;
- }
-
- bh_file_contents wasm_data = bh_file_read_contents(bh_heap_allocator(), &wasm_file);
- bh_file_close(&wasm_file);
-
- bh_buffer data;
- data.data = wasm_data.data;
- data.length = wasm_data.length;
- return onyx_run_wasm(data, argc - 1 - debug, argv + 1 + debug);
-}
+++ /dev/null
-// The sole job of the parser for Onyx is to submit nodes to the
-// entity heap for further processing. These nodes include things
-// such as procedure definitions, string literals, struct definitions
-// and declarations to be introduced into scopes.
-
-#include "lex.h"
-#include "errors.h"
-#include "parser.h"
-#include "utils.h"
-
-#define make_node(nclass, kind) onyx_ast_node_new(parser->allocator, sizeof(nclass), kind)
-// :LinearTokenDependent
-#define peek_token(ahead) (parser->curr + ahead)
-
-static AstNode error_node = { Ast_Kind_Error, 0, NULL, NULL };
-
-#define ENTITY_SUBMIT(node) (submit_entity_in_scope(parser, (AstNode *) (node), parser->current_scope, parser->package))
-#define ENTITY_SUBMIT_IN_SCOPE(node, scope) (submit_entity_in_scope(parser, (AstNode *) (node), scope, parser->package))
-
-void submit_entity_in_scope(OnyxParser* parser, AstNode* node, Scope* scope, Package* package) {
- if (bh_arr_length(parser->alternate_entity_placement_stack) == 0) {
- add_entities_for_node(NULL, node, scope, package);
-
- } else {
- bh_arr(Entity *) *entity_array = bh_arr_last(parser->alternate_entity_placement_stack);
- add_entities_for_node(entity_array, node, scope, package);
- }
-}
-
-// Parsing Utilities
-static void consume_token(OnyxParser* parser);
-static OnyxToken* expect_token(OnyxParser* parser, TokenType token_type);
-static b32 consume_token_if_next(OnyxParser* parser, TokenType token_type);
-static b32 next_tokens_are(OnyxParser* parser, i32 n, ...);
-static OnyxToken* find_matching_paren(OnyxToken* paren);
-
-static AstNumLit* parse_int_literal(OnyxParser* parser);
-static AstNumLit* parse_float_literal(OnyxParser* parser);
-static b32 parse_possible_struct_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret);
-static b32 parse_possible_array_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret);
-static b32 parse_possible_unary_field_access(OnyxParser* parser, AstTyped** ret);
-static void parse_arguments(OnyxParser* parser, TokenType end_token, Arguments* args);
-static AstTyped* parse_factor(OnyxParser* parser);
-static AstTyped* parse_compound_assignment(OnyxParser* parser, AstTyped* lhs);
-static AstTyped* parse_compound_expression(OnyxParser* parser, b32 assignment_allowed);
-static AstTyped* parse_expression(OnyxParser* parser, b32 assignment_allowed);
-static AstIfWhile* parse_if_stmt(OnyxParser* parser);
-static AstIfWhile* parse_while_stmt(OnyxParser* parser);
-static AstFor* parse_for_stmt(OnyxParser* parser);
-static AstSwitchCase* parse_case_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, char* block_name);
-static AstNode* parse_statement(OnyxParser* parser);
-static void parse_polymorphic_variable(OnyxParser* parser, AstType*** next_insertion);
-static AstType* parse_type(OnyxParser* parser);
-static AstTypeOf* parse_typeof(OnyxParser* parser);
-static AstStructType* parse_struct(OnyxParser* parser);
-static AstInterface* parse_interface(OnyxParser* parser);
-static AstConstraint* parse_constraint(OnyxParser* parser);
-static void parse_constraints(OnyxParser* parser, ConstraintContext *constraints);
-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_no_consume(OnyxParser* parser);
-static b32 parse_possible_function_definition(OnyxParser* parser, AstTyped** ret);
-static b32 parse_possible_quick_function_definition_no_consume(OnyxParser* parser);
-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 AstMemRes* parse_memory_reservation(OnyxParser* parser, OnyxToken* symbol, b32 thread_local);
-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, Error_Critical, "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;
-}
-
-
-static void expect_no_stored_tags_pos(OnyxParser *parser, OnyxFilePos pos) {
- if (bh_arr_length(parser->stored_tags) > 0) {
- onyx_report_error(pos, Error_Critical, "#tag is not allowed on this element.");
- parser->hit_unexpected_token = 1;
- }
-}
-
-static void expect_no_stored_tags(OnyxParser *parser) {
- expect_no_stored_tags_pos(parser, parser->curr->pos);
-}
-
-static void flush_stored_tags(OnyxParser *parser, bh_arr(AstTyped *) *out_arr) {
- //
- // When tag_depth > 0, no tags will be added to the element.
- // This happens if you have a something like so,
- //
- // #tag "asdf"
- // #tag handler.{ (x: i32) => x * 2 }
- // foo :: () { ... }
- //
- // In this situation, the inner procedure defined in the second
- // tag should NOT consume the "asdf" tag.
- //
- if (bh_arr_length(parser->stored_tags) == 0 || parser->tag_depth > 0) return;
-
- bh_arr(AstTyped *) arr = *out_arr;
-
- if (arr == NULL) {
- bh_arr_new(parser->allocator, arr, bh_arr_length(parser->stored_tags));
- }
-
- bh_arr_each(AstTyped *, pexpr, parser->stored_tags) {
- bh_arr_push(arr, *pexpr);
- }
-
- bh_arr_clear(parser->stored_tags);
-
- *out_arr = arr;
-}
-
-
-// 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;
-
- int_node->type_node = (AstType *) &basic_type_int_unsized;
-
- // NOTE: Hex literals are unsigned.
- if (int_node->token->length >= 2 && int_node->token->text[1] == 'x') {
- int_node->was_hex_literal = 1;
- }
-
- 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;
-
- //
- // This has a weird condition to avoid the problem of using a quick function as an argument:
- // f(x => x + 1)
- // This shouldn't be a named argument, but this should:
- // f(g = x => x + 1)
- //
- if (next_tokens_are(parser, 2, Token_Type_Symbol, '=') && peek_token(2)->type != '>') {
- 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)) {
- ENTITY_SUBMIT(retval);
- break;
- }
- if (parse_possible_quick_function_definition(parser, &retval)) {
- ENTITY_SUBMIT(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: {
- if (parse_possible_quick_function_definition(parser, &retval)) {
- ENTITY_SUBMIT(retval);
- break;
- }
-
- 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->data_id = 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, Error_Critical, "Expected '{' after 'do', got '%s'.", token_name(parser->curr->type));
- retval = NULL;
- break;
- }
-
- do_block->block = parse_block(parser, 1, NULL);
-
- retval = (AstTyped *) do_block;
- break;
- }
-
- case '[': {
- AstType *type = parse_type(parser);
- retval = (AstTyped *) type;
- break;
- }
-
- case '$': {
- AstType **tmp = (AstType **) &retval;
- parse_polymorphic_variable(parser, &tmp);
- 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_expr = parse_expression(parser, 0);
- fc->type = type_make_slice(parser->allocator, &basic_types[Basic_Kind_U8]);
-
- if (parser->current_function_stack && bh_arr_length(parser->current_function_stack) > 0) {
- bh_arr_push(bh_arr_last(parser->current_function_stack)->nodes_that_need_entities_after_clone, (AstNode *) fc);
-
- } else {
- 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->data_id = 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, Error_Critical, "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 = (AstFunction *) 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 = (AstType *) parse_expression(parser, 0);
-
- 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, "quote")) {
- 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, NULL);
- ((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 (next_tokens_are(parser, 2, '#', '(')) {
- OnyxToken* code_token = expect_token(parser, '#');
- expect_token(parser, '(');
-
- 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;
-
- code_block->code = (AstNode *) parse_expression(parser, 0);
-
- expect_token(parser, ')');
-
- retval = (AstTyped *) code_block;
- break;
- }
- else if (parse_possible_directive(parser, "unquote")) {
- 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;
- }
- else if (parse_possible_directive(parser, "cstr")) {
- // Copy pasted from above.
- AstStrLit* str_node = make_node(AstStrLit, Ast_Kind_StrLit);
- str_node->token = expect_token(parser, Token_Type_Literal_String);
- str_node->data_id = 0;
- str_node->flags |= Ast_Flag_Comptime;
- str_node->is_cstr = 1;
-
- ENTITY_SUBMIT(str_node);
-
- retval = (AstTyped *) str_node;
- break;
- }
-
- onyx_report_error(parser->curr->pos, Error_Critical, "Invalid directive in expression.");
- return NULL;
- }
-
- default:
- no_match:
- onyx_report_error(parser->curr->pos, Error_Critical, "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 '(': {
- if (!parser->parse_calls) goto factor_parsed;
-
- 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_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;
- 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;
-
- case Binary_Op_Method_Call: return 10;
-
- 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;
- case Token_Type_Right_Arrow: return Binary_Op_Method_Call;
- 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_Method_Call) bin_op = make_node(AstBinaryOp, Ast_Kind_Method_Call);
- 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, NULL);
-
- 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, NULL);
-
- 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, NULL);
- 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;
-
- if (parse_possible_directive(parser, "bottom_test")) {
- while_node->bottom_test = 1;
- }
-
- 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, NULL);
-
- if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
- while_node->false_stmt = parse_block(parser, 1, NULL);
- }
-
- 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 (parse_possible_directive(parser, "no_close")) {
- for_node->no_close = 1;
- }
-
- if (consume_token_if_next(parser, '^')) {
- for_node->by_pointer = 1;
- }
-
- if (next_tokens_are(parser, 2, Token_Type_Symbol, ':')) {
- 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, ':');
- } else {
- // HACK
- static char it_name[] = "it ";
- static OnyxToken it_token = { Token_Type_Symbol, 2, it_name, { 0 } };
-
- AstLocal* var_node = make_local(parser->allocator, &it_token, NULL);
- for_node->var = var_node;
- }
-
- for_node->iter = parse_expression(parser, 1);
- for_node->stmt = parse_block(parser, 1, NULL);
-
- return for_node;
-}
-
-static AstSwitchCase* parse_case_stmt(OnyxParser* parser) {
- AstSwitchCase *sc_node = make_node(AstSwitchCase, Ast_Kind_Switch_Case);
- sc_node->token = expect_token(parser, Token_Type_Keyword_Case);
-
- if (parse_possible_directive(parser, "default")) {
- sc_node->is_default = 1;
-
- } else {
- bh_arr_new(global_heap_allocator, sc_node->values, 1);
-
- AstTyped* value = parse_expression(parser, 1);
- bh_arr_push(sc_node->values, value);
- while (consume_token_if_next(parser, ',')) {
- if (parser->hit_unexpected_token) return sc_node;
-
- value = parse_expression(parser, 1);
- bh_arr_push(sc_node->values, value);
- }
- }
-
- sc_node->block = parse_block(parser, 1, NULL);
-
- return sc_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);
-
- 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;
-
- switch_node->case_block = parse_block(parser, 1, NULL);
- 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 == '\'') 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);
- AstNode* sym_node = make_symbol(parser->allocator, local_sym);
- bh_arr_push(local_compound->exprs, (AstTyped *) sym_node);
-
- if (!consume_token_if_next(parser, '\'')) {
- 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;
- }
-
- 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 == ',' ||
- (peek_token(1)->type == '\'' && peek_token(2)->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 == ':') {
- bh_arr_push(parser->current_symbol_stack, symbol);
- AstBinding* binding = parse_top_level_binding(parser, symbol);
- bh_arr_pop(parser->current_symbol_stack);
- 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.as_name = expect_token(parser, Token_Type_Symbol);
- qu.symbol_name = qu.as_name;
-
- if (consume_token_if_next(parser, ':')) {
- expect_token(parser, ':');
- qu.symbol_name = expect_token(parser, Token_Type_Symbol);
- }
-
- 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, NULL);
- 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);
- if (retval->kind == Ast_Kind_Call || retval->kind == Ast_Kind_Method_Call) {
- if (parser->curr->type == '{') {
- AstCodeBlock* code_block = make_node(AstCodeBlock, Ast_Kind_Code_Block);
- code_block->token = parser->curr;
- code_block->type_node = builtin_code_type;
-
- code_block->code = (AstNode *) parse_block(parser, 1, NULL);
- ((AstBlock *) code_block->code)->rules = Block_Rule_Code_Block;
-
- AstCall *dest = (AstCall *) retval;
- if (dest->kind == Ast_Kind_Method_Call) {
- dest = (AstCall *) ((AstBinaryOp *) dest)->right;
- if (dest->kind != Ast_Kind_Call) {
- onyx_report_error(retval->token->pos, Error_Critical, "Expected function call on right side of '->'.");
- needs_semicolon = 0;
- break;
- }
- }
-
- bh_arr_push(dest->args.values, (AstTyped *) make_argument(context.ast_alloc, (AstTyped *) code_block));
- needs_semicolon = 0;
- }
- }
- 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_Case:
- needs_semicolon = 0;
- retval = (AstNode *) parse_case_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")) {
- // :LinearTokenDependent
- OnyxToken* directive_token = parser->curr - 2;
-
- OnyxToken* sym_token = bh_alloc_item(parser->allocator, OnyxToken);
- sym_token->type = Token_Type_Symbol;
- sym_token->length = 15;
- sym_token->text = bh_strdup(parser->allocator, "__saved_context ");
- sym_token->pos = ((OnyxFilePos) {0});
-
- AstNode *sym_node = make_symbol(parser->allocator, sym_token);
-
- AstLocal* context_tmp = make_local(parser->allocator, sym_token, NULL);
- retval = (AstNode *) context_tmp;
-
- AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
- assignment->token = directive_token;
- assignment->operation = Binary_Op_Assign;
- assignment->left = (AstTyped *) sym_node;
- assignment->right = builtin_context_variable;
- context_tmp->next = (AstNode *) assignment;
-
- AstBinaryOp* assignment2 = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
- assignment2->token = directive_token + 1;
- assignment2->operation = Binary_Op_Assign;
- assignment2->left = builtin_context_variable;
- assignment2->right = (AstTyped *) sym_node;
-
- AstDefer* defer_node = make_node(AstDefer, Ast_Kind_Defer);
- defer_node->token = directive_token;
- defer_node->stmt = (AstNode *) assignment2;
- assignment->next = (AstNode *) defer_node;
-
- AstBlock* context_block = parse_block(parser, 1, NULL);
- defer_node->next = context_block->body;
- context_block->body = (AstNode *) context_tmp;
-
- needs_semicolon = 0;
- break;
- }
-
- if (next_tokens_are(parser, 2, '#', Token_Type_Keyword_If)) {
- AstIf* static_if = parse_static_if_stmt(parser, 1);
-
- assert(parser->current_function_stack && bh_arr_length(parser->current_function_stack) > 0);
- bh_arr_push(bh_arr_last(parser->current_function_stack)->nodes_that_need_entities_after_clone, (AstNode *) static_if);
-
- needs_semicolon = 0;
- retval = (AstNode *) static_if;
- break;
- }
-
- if (parse_possible_directive(parser, "persist")) {
- b32 thread_local = parse_possible_directive(parser, "thread_local");
-
- OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
- AstMemRes* memres = parse_memory_reservation(parser, symbol, thread_local);
-
- AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
- binding->token = memres->token;
- binding->node = (AstNode *) memres;
- ENTITY_SUBMIT(binding);
- break;
- }
-
- if (parse_possible_directive(parser, "remove")) {
- // :LinearTokenDependent
- AstDirectiveRemove *remove = make_node(AstDirectiveRemove, Ast_Kind_Directive_Remove);
- remove->token = parser->curr - 2;
- retval = (AstNode *) remove;
- 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, char* block_name) {
- 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);
- block->binding_scope->name = block_name;
- 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, Error_Critical, "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 '$': {
- 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 == '(' && parser->parse_calls) {
- 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_expression(parser, 0);
- 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;
- }
-
- //
- // I don't think any of these cases are necesary any more?
- 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:
- case '-': {
- *next_insertion = (AstType *) parse_expression(parser, 0);
- 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, Error_Critical, "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 void struct_type_create_scope(OnyxParser *parser, AstStructType *s_node) {
- 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* current_symbol = bh_arr_last(parser->current_symbol_stack);
- s_node->scope->name = bh_aprintf(global_heap_allocator, "%b", current_symbol->text, current_symbol->length);
- }
-}
-
-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;
-
- flush_stored_tags(parser, &s_node->meta_tags);
-
- // Parse polymorphic parameters
- 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;
- }
-
- // Parse constraints clause
- if (parser->curr->type == Token_Type_Keyword_Where) {
- parse_constraints(parser, &s_node->constraints);
- }
-
- bh_arr_new(global_heap_allocator, s_node->members, 4);
-
- // Parse directives
- while (parser->curr->type == '#') {
- if (parser->hit_unexpected_token) return NULL;
-
- if (parse_possible_directive(parser, "union")) s_node->is_union = 1;
-
- else if (parse_possible_directive(parser, "pack")) s_node->is_packed = 1;
-
- else if (parse_possible_directive(parser, "align")) {
- s_node->min_alignment_ = parse_expression(parser, 0);
- }
-
- else if (parse_possible_directive(parser, "size")) {
- s_node->min_size_ = parse_expression(parser, 0);
- }
-
- else {
- OnyxToken* directive_token = expect_token(parser, '#');
- OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
-
- onyx_report_error(directive_token->pos, Error_Critical, "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;
-
- if (parse_possible_directive(parser, "persist")) {
- struct_type_create_scope(parser, s_node);
-
- b32 thread_local = parse_possible_directive(parser, "thread_local");
-
- OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
- AstMemRes* memres = parse_memory_reservation(parser, symbol, thread_local);
- consume_token_if_next(parser, ';');
-
- AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
- binding->token = memres->token;
- binding->node = (AstNode *) memres;
- ENTITY_SUBMIT(binding);
- continue;
- }
-
- if (next_tokens_are(parser, 3, Token_Type_Symbol, ':', ':')) {
- struct_type_create_scope(parser, s_node);
-
- 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, ';');
- continue;
- }
-
- bh_arr(AstTyped *) meta_tags=NULL;
- while (parse_possible_directive(parser, "tag")) {
- if (meta_tags == NULL) bh_arr_new(global_heap_allocator, meta_tags, 1);
-
- parser->tag_depth += 1;
-
- do {
- AstTyped* expr = parse_expression(parser, 0);
- bh_arr_push(meta_tags, expr);
- } while (consume_token_if_next(parser, ','));
-
- parser->tag_depth -= 1;
- }
-
- member_is_used = consume_token_if_next(parser, Token_Type_Keyword_Use);
-
- 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, Error_Critical, "'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, Error_Critical, "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;
- mem->meta_tags = meta_tags;
-
- if (member_is_used) mem->is_used = 1;
-
- bh_arr_push(s_node->members, mem);
- }
-
- if (peek_token(0)->type != '}') {
- 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 {
- ENTITY_SUBMIT(s_node);
- return s_node;
- }
-}
-
-static AstInterface* parse_interface(OnyxParser* parser) {
- AstInterface *interface = make_node(AstInterface, Ast_Kind_Interface);
- interface->token = expect_token(parser, Token_Type_Keyword_Interface);
-
- bh_arr_new(global_heap_allocator, interface->params, 2);
-
- expect_token(parser, '(');
- while (!consume_token_if_next(parser, ')')) {
- if (parser->hit_unexpected_token) return interface;
-
- InterfaceParam ip;
- ip.value_token = expect_token(parser, Token_Type_Symbol);
- expect_token(parser, ':');
- expect_token(parser, '$');
- ip.type_token = expect_token(parser, Token_Type_Symbol);
-
- bh_arr_push(interface->params, ip);
-
- if (parser->curr->type != ')')
- expect_token(parser, ',');
- }
-
- bh_arr_new(global_heap_allocator, interface->exprs, 2);
-
- expect_token(parser, '{');
- while (!consume_token_if_next(parser, '}')) {
- if (parser->hit_unexpected_token) return interface;
-
- InterfaceConstraint ic = {0};
- if (parse_possible_directive(parser, "not")) {
- ic.invert_condition = 1;
- }
-
- if (consume_token_if_next(parser, '{')) {
- ic.expr = parse_expression(parser, 0);
-
- expect_token(parser, '}');
- expect_token(parser, Token_Type_Right_Arrow);
-
- ic.expected_type_expr = parse_type(parser);
-
- } else {
- ic.expr = parse_expression(parser, 0);
- }
-
- bh_arr_push(interface->exprs, ic);
-
- expect_token(parser, ';');
- }
-
- return interface;
-}
-
-static AstConstraint* parse_constraint(OnyxParser* parser) {
- AstConstraint* constraint = make_node(AstConstraint, Ast_Kind_Constraint);
-
- parser->parse_calls = 0;
- constraint->interface = (AstInterface *) parse_factor(parser);
- parser->parse_calls = 1;
-
- constraint->token = constraint->interface->token;
-
- bh_arr_new(global_heap_allocator, constraint->type_args, 2);
-
- expect_token(parser, '(');
- while (!consume_token_if_next(parser, ')')) {
- if (parser->hit_unexpected_token) return constraint;
-
- AstType* type_node = parse_type(parser);
- bh_arr_push(constraint->type_args, type_node);
-
- if (parser->curr->type != ')')
- expect_token(parser, ',');
- }
-
- return constraint;
-}
-
-static void parse_constraints(OnyxParser* parser, ConstraintContext *out_constraints) {
- bh_arr_new(global_heap_allocator, out_constraints->constraints, 2);
-
- expect_token(parser, Token_Type_Keyword_Where);
-
- do {
- AstConstraint *constraint = parse_constraint(parser);
- if (parser->hit_unexpected_token) return;
-
- bh_arr_push(out_constraints->constraints, constraint);
- } while (consume_token_if_next(parser, ','));
-}
-
-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);
-
- bh_arr(AstParam) param_buffer=NULL;
- bh_arr_new(global_heap_allocator, param_buffer, 2);
-
- OnyxToken* symbol;
- while (!consume_token_if_next(parser, ')')) {
- do {
- if (parser->hit_unexpected_token) return;
-
- b32 param_use = 0;
- b32 param_is_baked = 0;
- 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);
-
- 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.is_used = 1;
- param_use = 0;
- }
-
- if (param_is_baked) {
- curr_param.is_baked = 1;
- param_is_baked = 0;
- }
-
- bh_arr_push(param_buffer, curr_param);
- } while (consume_token_if_next(parser, ','));
-
- expect_token(parser, ':');
-
- VarArgKind vararg_kind=VA_Kind_Not_VA;
- AstType* type_node=NULL;
- AstTyped* default_value=NULL;
-
- if (parser->curr->type != '=') {
- if (consume_token_if_next(parser, Token_Type_Dot_Dot)) {
- if (consume_token_if_next(parser, '.')) vararg_kind = VA_Kind_Untyped;
- else vararg_kind = VA_Kind_Typed;
- }
-
- if (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);
- type_node = parse_type(parser);
- i32 new_len = bh_arr_length(*parser->polymorph_context.poly_params);
-
- if (vararg_kind == VA_Kind_Typed) {
- AstVarArgType* va_type = make_node(AstVarArgType, Ast_Kind_VarArg_Type);
- va_type->elem = type_node;
- va_type->token = type_node->token;
- type_node = (AstType *) va_type;
- }
-
- fori (i, 0, new_len - old_len) {
- (*parser->polymorph_context.poly_params)[old_len + i].type_expr = type_node;
- (*parser->polymorph_context.poly_params)[old_len + i].idx = param_idx;
- }
- }
- }
-
- if (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;
- default_value = (AstTyped *) cs;
-
- } else {
- default_value = parse_expression(parser, 0);
- }
- }
-
- bh_arr_each(AstParam, param, param_buffer) {
- param->vararg_kind = vararg_kind;
- param->local->type_node = type_node;
- param->default_value = default_value;
-
- if (param->is_baked) {
- bh_arr(AstPolyParam) pv = *parser->polymorph_context.poly_params;
- bh_arr_push(pv, ((AstPolyParam) {
- .kind = PPK_Baked_Value,
- .idx = param_idx,
-
- .poly_sym = (AstNode *) param->local,
- .type_expr = type_node,
- }));
-
- *parser->polymorph_context.poly_params = pv;
- }
-
- bh_arr_push(func->params, *param);
- param_idx++;
- }
-
- bh_arr_clear(param_buffer);
-
- if (parser->curr->type != ')')
- expect_token(parser, ',');
- }
-
- bh_arr_free(param_buffer);
- return;
-}
-
-static AstOverloadedFunction* parse_overloaded_function(OnyxParser* parser, OnyxToken* token) {
- b32 locked = 0;
- if (parse_possible_directive(parser, "locked")) {
- locked = 1;
- }
-
- b32 local = 0;
- if (parse_possible_directive(parser, "local")) {
- local = 1;
- }
-
- // This could be checked elsewhere?
- if (locked && local) {
- onyx_report_error(token->pos, Error_Critical, "Only one of '#locked' and '#local' can because use at a time.");
- }
-
- expect_token(parser, '{');
-
- AstOverloadedFunction* ofunc = make_node(AstOverloadedFunction, Ast_Kind_Overloaded_Function);
- ofunc->token = token;
- ofunc->flags |= Ast_Flag_Comptime;
- ofunc->locked = locked;
- ofunc->only_local_functions = local;
-
- 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_push(parser->current_function_stack, func_def);
-
- flush_stored_tags(parser, &func_def->tags);
-
- 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);
- // defer bh_arr_free(polymorphic_vars);
-
- 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;
-
- char* name = NULL;
- if (bh_arr_length(parser->current_symbol_stack) > 0) {
- OnyxToken *current_symbol = bh_arr_last(parser->current_symbol_stack);
- name = bh_aprintf(global_heap_allocator, "%b", current_symbol->text, current_symbol->length);
- }
-
- if (consume_token_if_next(parser, '=')) {
- expect_token(parser, '>');
- func_def->return_type = (AstType *) &basic_type_auto_return;
-
- if (parser->curr->type == '{') {
- func_def->body = parse_block(parser, 1, name);
-
- } else {
- AstTyped* returned_value = parse_compound_expression(parser, 0);
- if (returned_value == NULL) goto function_defined;
-
- AstReturn* return_node = make_node(AstReturn, Ast_Kind_Return);
- return_node->token = returned_value->token;
- return_node->expr = returned_value;
-
- AstBlock* body_block = make_node(AstBlock, Ast_Kind_Block);
- body_block->token = returned_value->token;
- body_block->body = (AstNode *) return_node;
-
- func_def->body = body_block;
- }
-
- goto function_defined;
- }
-
- 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);
- }
- }
-
- if (parser->curr->type == Token_Type_Keyword_Where) {
- parse_constraints(parser, &func_def->constraints);
- }
-
- while (parser->curr->type == '#') {
- if (parse_possible_directive(parser, "intrinsic")) {
- func_def->is_intrinsic = 1;
-
- 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->is_foreign = 1;
- }
-
- // 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, Error_Critical, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
- }
- }
-
- func_def->body = parse_block(parser, 1, name);
-
- // :LinearTokenDependent
- func_def->closing_brace = parser->curr - 1;
-
-function_defined:
- if (bh_arr_length(polymorphic_vars) > 0) {
- func_def->kind = Ast_Kind_Polymorphic_Proc;
- func_def->poly_params = polymorphic_vars;
-
- } else {
- bh_arr_free(polymorphic_vars);
- }
-
- bh_arr_pop(parser->current_function_stack);
- return func_def;
-}
-
-static b32 parse_possible_function_definition_no_consume(OnyxParser* parser) {
- if (parser->curr->type == '(') {
- OnyxToken* matching_paren = find_matching_paren(parser->curr);
- if (matching_paren == NULL) return 0;
-
- if (next_tokens_are(parser, 4, '(', ')', '=', '>')) 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
- && token_after_paren->type != Token_Type_Keyword_Where
- && (token_after_paren->type != '=' || (token_after_paren + 1)->type != '>'))
- 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;
-
- return 1;
- }
-
- return 0;
-}
-
-static b32 parse_possible_function_definition(OnyxParser* parser, AstTyped** ret) {
- if (parse_possible_function_definition_no_consume(parser)) {
- OnyxToken* proc_token = parser->curr;
- AstFunction* func_node = parse_function_definition(parser, proc_token);
- *ret = (AstTyped *) func_node;
-
- return 1;
- }
-
- return 0;
-}
-
-typedef struct QuickParam {
- OnyxToken* token;
- b32 is_baked;
-} QuickParam;
-
-static b32 parse_possible_quick_function_definition_no_consume(OnyxParser* parser) {
- //
- // x => x + 1 case.
- if (next_tokens_are(parser, 3, Token_Type_Symbol, '=', '>')) {
- return 1;
- }
-
- 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;
-
- return 1;
-}
-
-static b32 parse_possible_quick_function_definition(OnyxParser* parser, AstTyped** ret) {
- if (!parse_possible_quick_function_definition_no_consume(parser)) return 0;
-
- bh_arr(QuickParam) params=NULL;
- bh_arr_new(global_heap_allocator, params, 4);
- OnyxToken* proc_token;
-
- if (parser->curr->type == Token_Type_Symbol) {
- QuickParam param = { 0 };
- param.token = expect_token(parser, Token_Type_Symbol);
- proc_token = param.token;
- bh_arr_push(params, param);
-
- } else {
- proc_token = expect_token(parser, '(');
- while (parser->curr->type != ')') {
- if (parser->hit_unexpected_token) return 0;
-
- QuickParam param = { 0 };
- if (consume_token_if_next(parser, '$')) param.is_baked = 1;
- param.token = expect_token(parser, Token_Type_Symbol);
-
- bh_arr_push(params, param);
-
- 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(QuickParam, param, params) {
- char text[512];
- memset(text, 0, 512);
- strncat(text, "__type_", 511);
- token_toggle_end(param->token);
- strncat(text, param->token->text, 511);
- token_toggle_end(param->token);
-
- OnyxToken* new_token = bh_alloc(parser->allocator, sizeof(OnyxToken));
- new_token->type = Token_Type_Symbol;
- new_token->length = 7 + param->token->length;
- new_token->text = bh_strdup(parser->allocator, text);
- new_token->pos = param->token->pos;
-
- AstNode* type_node = make_symbol(parser->allocator, new_token);
- bh_arr_push(poly_params, type_node);
- }
-
- AstFunction* poly_proc = make_node(AstFunction, Ast_Kind_Polymorphic_Proc);
-
- bh_arr_new(global_heap_allocator, poly_proc->params, bh_arr_length(params));
- fori (i, 0, bh_arr_length(params)) {
- AstLocal* param_local = make_local(parser->allocator, params[i].token, (AstType *) poly_params[i]);
- param_local->kind = Ast_Kind_Param;
-
- bh_arr_push(poly_proc->params, ((AstParam) {
- .local = param_local,
- .default_value = NULL,
-
- .vararg_kind = 0,
- .use_processed = 0,
- }));
- }
-
- AstBlock* body_block;
- AstType* return_type;
- bh_arr_push(parser->current_function_stack, poly_proc);
-
- if (parser->curr->type == '{') {
- char* name = NULL;
- if (bh_arr_length(parser->current_symbol_stack) > 0) {
- OnyxToken *current_symbol = bh_arr_last(parser->current_symbol_stack);
- name = bh_aprintf(global_heap_allocator, "%b", current_symbol->text, current_symbol->length);
- }
-
- body_block = parse_block(parser, 1, name);
- 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;
- }
-
- poly_proc->token = proc_token;
- poly_proc->body = body_block;
- poly_proc->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,
- }));
-
- if (params[i].is_baked) {
- // This is not handled currently, as you cannot say f :: ($x: $T) yet, which is what this would have to do.
- }
- }
-
- *ret = (AstTyped *) poly_proc;
-
- bh_arr_pop(parser->current_function_stack);
- bh_arr_free(params);
- bh_arr_free(poly_params);
- return 1;
-}
-
-static AstTyped* parse_global_declaration(OnyxParser* parser) {
- expect_no_stored_tags(parser);
-
- AstGlobal* global_node = make_node(AstGlobal, Ast_Kind_Global);
- global_node->token = expect_token(parser, Token_Type_Keyword_Global);
-
- global_node->type_node = parse_type(parser);
-
- ENTITY_SUBMIT(global_node);
-
- return (AstTyped *) global_node;
-}
-
-static AstEnumType* parse_enum_declaration(OnyxParser* parser) {
- expect_no_stored_tags(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 (parser->hit_unexpected_token) return enum_node;
-
- if (parse_possible_directive(parser, "flags")) {
- enum_node->is_flags = 1;
- } else {
- OnyxToken* directive_token = expect_token(parser, '#');
- OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
-
- onyx_report_error(directive_token->pos, Error_Critical, "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, ':');
- evalue->value = parse_expression(parser, 0);
- }
-
- 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) {
- expect_no_stored_tags(parser);
-
- AstIf* static_if_node = make_node(AstIf, Ast_Kind_Static_If);
- static_if_node->token = expect_token(parser, '#');
- static_if_node->defined_in_scope = parser->current_scope;
- 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, NULL);
-
- } 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, NULL);
-
- } 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 AstMemRes* parse_memory_reservation(OnyxParser* parser, OnyxToken* symbol, b32 threadlocal) {
- expect_token(parser, ':');
-
- expect_no_stored_tags(parser);
-
- AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres);
- memres->threadlocal = threadlocal;
- 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);
- return memres;
-}
-
-static AstMacro* parse_macro(OnyxParser* parser) {
- expect_no_stored_tags(parser);
-
- AstMacro* macro = make_node(AstMacro, Ast_Kind_Macro);
- macro->token = expect_token(parser, Token_Type_Keyword_Macro);
-
- if (parse_possible_function_definition(parser, ¯o->body)) {
- ENTITY_SUBMIT(macro);
- return macro;
- }
-
- if (parse_possible_quick_function_definition(parser, ¯o->body)) {
- ENTITY_SUBMIT(macro);
- return macro;
- }
-
- onyx_report_error(parser->curr->pos, Error_Critical, "'macro' expects to be followed by a producure definition.");
- return NULL;
-}
-
-static AstDirectiveInit* parse_init_directive(OnyxParser *parser, OnyxToken *token) {
- AstDirectiveInit *init = make_node(AstDirectiveInit, Ast_Kind_Directive_Init);
- init->token = token;
-
- parser->parse_calls = 0;
- while (parse_possible_directive(parser, "after")) {
- if (parser->hit_unexpected_token) return init;
- if (init->dependencies == NULL) bh_arr_new(global_heap_allocator, init->dependencies, 2);
-
- AstTyped *dependency = parse_expression(parser, 0);
- bh_arr_push(init->dependencies, (AstDirectiveInit *) dependency);
- }
- parser->parse_calls = 1;
-
- init->init_proc = parse_expression(parser, 0);
- ENTITY_SUBMIT(init);
- return init;
-}
-
-static AstForeignBlock* parse_foreign_block(OnyxParser* parser, OnyxToken *token) {
- // :LinearTokenDependent
- AstForeignBlock *fb = make_node(AstForeignBlock, Ast_Kind_Foreign_Block);
- fb->token = token;
- fb->module_name = expect_token(parser, Token_Type_Literal_String);
-
- //
- // This has a fun implication that there cannot be foreign blocks in the builtin
- // or type_info packages, as those are loaded before foreign_block_type has a value.
- fb->type_node = foreign_block_type;
-
- bh_arr_new(global_heap_allocator, fb->captured_entities, 4);
- bh_arr_push(parser->alternate_entity_placement_stack, &fb->captured_entities);
-
- expect_token(parser, '{');
- parse_top_level_statements_until(parser, '}');
- expect_token(parser, '}');
-
- bh_arr_pop(parser->alternate_entity_placement_stack);
- ENTITY_SUBMIT(fb);
-
- return fb;
-}
-
-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_Interface) return (AstTyped *) parse_interface(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 (parser->curr->type == '#') {
- 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;
- }
-
- if (parse_possible_directive(parser, "init")) {
- // :LinearTokenDependent
- AstDirectiveInit *init = parse_init_directive(parser, parser->curr - 2);
- return (AstTyped *) init;
- }
-
- if (parse_possible_directive(parser, "distinct")) {
- // :LinearTokenDependent
- AstDistinctType *distinct = make_node(AstDistinctType, Ast_Kind_Distinct_Type);
- distinct->token = parser->curr - 2;
- distinct->base_type = parse_type(parser);
- return (AstTyped *) distinct;
- }
-
- if (parse_possible_directive(parser, "foreign")) {
- AstForeignBlock *foreign = parse_foreign_block(parser, parser->curr - 2);
- return (AstTyped *) foreign;
- }
- }
-
- return parse_expression(parser, 1);
-}
-
-static char* generate_name_within_scope(OnyxParser* parser, OnyxToken* symbol) {
- char name[512];
- memset(name, 0, 512);
-
- bh_arr(char *) names=NULL;
- bh_arr_new(global_heap_allocator, names, 4);
-
- Scope* scope = parser->current_scope;
- while (scope != NULL) {
- bh_arr_push(names, scope->name);
- scope = scope->parent;
- }
-
- bh_arr_each(char *, n, names) {
- if (*n == NULL) continue;
-
- strncat(name, *n, 511);
- strncat(name, ".", 511);
- }
- bh_arr_free(names);
-
- return bh_aprintf(global_heap_allocator, "%s%b", name, symbol->text, symbol->length);
-}
-
-static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol) {
- OnyxToken *after_second_colon = expect_token(parser, ':');
- if (after_second_colon) after_second_colon += 1;
-
- AstTyped* node = parse_top_level_expression(parser);
- if (parser->hit_unexpected_token || node == NULL)
- return NULL;
-
- switch (node->kind) {
- case Ast_Kind_Function:
- case Ast_Kind_Polymorphic_Proc: {
- AstFunction* func = (AstFunction *) node;
-
- if (func->intrinsic_name == NULL) func->intrinsic_name = symbol;
-
- func->name = generate_name_within_scope(parser, symbol);
- break;
- }
-
- case Ast_Kind_Macro: {
- AstMacro* macro = (AstMacro *) node;
-
- AstFunction* func = (AstFunction *) macro->body;
- func->name = generate_name_within_scope(parser, symbol);
- break;
- }
-
- case Ast_Kind_Directive_Init: break;
-
- case Ast_Kind_Global: ((AstGlobal *) node)->name = generate_name_within_scope(parser, symbol);
-
- case Ast_Kind_Overloaded_Function:
- case Ast_Kind_StrLit:
- break;
-
- case Ast_Kind_Interface:
- case Ast_Kind_Struct_Type:
- case Ast_Kind_Poly_Struct_Type:
- case Ast_Kind_Enum_Type:
- case Ast_Kind_Distinct_Type:
- ((AstStructType *) node)->name = generate_name_within_scope(parser, symbol);
- goto default_case;
-
- case Ast_Kind_Type_Alias:
- node->token = symbol;
- goto default_case;
-
- case Ast_Kind_Package: goto default_case;
- case Ast_Kind_NumLit: goto default_case;
-
- default: {
- if (!node_is_type((AstNode *) node)) {
- AstAlias* alias = make_node(AstAlias, Ast_Kind_Alias);
- alias->token = node->token;
- alias->alias = node;
- node = (AstTyped *) alias;
- }
-
-default_case:
- ENTITY_SUBMIT(node);
- }
- }
-
- AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
- binding->token = symbol;
- binding->node = (AstNode *) node;
-
- if (after_second_colon) expect_no_stored_tags_pos(parser, after_second_colon->pos);
- 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 (next_tokens_are(parser, 2, '#', Token_Type_Keyword_Package)) {
- consume_tokens(parser, 2);
- 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, "local")) {
- 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);
-
- if (next_tokens_are(parser, 2, ':', ':')) {
- expect_token(parser, ':');
-
- bh_arr_push(parser->current_symbol_stack, symbol);
- binding = parse_top_level_binding(parser, symbol);
- bh_arr_pop(parser->current_symbol_stack);
-
- if (binding != NULL) binding->flags |= private_kind;
-
- goto submit_binding_to_entities;
- }
-
- AstMemRes* memres = parse_memory_reservation(parser, symbol, 0);
-
- binding = make_node(AstBinding, Ast_Kind_Binding);
- binding->token = symbol;
- binding->flags |= private_kind;
- binding->node = (AstNode *) memres;
-
- goto submit_binding_to_entities;
- }
-
- case '(': {
- AstTyped *retval = NULL;
- if (parse_possible_function_definition(parser, &retval)) {
- ENTITY_SUBMIT(retval);
- return;
- }
- if (parse_possible_quick_function_definition(parser, &retval)) {
- ENTITY_SUBMIT(retval);
- return;
- }
- break;
- }
-
- 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;
- include->name_node = parse_expression(parser, 0);
-
- ENTITY_SUBMIT(include);
- return;
- }
- else if (parse_possible_directive(parser, "load_all")) {
- AstInclude* include = make_node(AstInclude, Ast_Kind_Load_All);
- include->token = dir_token;
- include->name_node = parse_expression(parser, 0);
-
- 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;
- include->name_node = parse_expression(parser, 0);
-
- ENTITY_SUBMIT(include);
- return;
- }
- else if (parse_possible_directive(parser, "library_path")) {
- AstInclude* include = make_node(AstInclude, Ast_Kind_Library_Path);
- include->token = dir_token;
- include->name_node = parse_expression(parser, 0);
-
- 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, "foreign")) {
- parse_foreign_block(parser, parser->curr - 2);
- return;
- }
- else if (parse_possible_directive(parser, "operator")) {
- AstDirectiveOperator *operator = make_node(AstDirectiveOperator, Ast_Kind_Directive_Operator);
- operator->token = dir_token;
-
- // These cases have to happen first because these are not necessarily "binary operators",
- // they are just things that I want to be able to overload. []= is technically a ternary
- // operator so all these things are horribly named anyway.
- if (next_tokens_are(parser, 3, '^', '[', ']')) {
- consume_tokens(parser, 3);
- operator->operator = Binary_Op_Ptr_Subscript;
- goto operator_determined;
- }
-
- if (next_tokens_are(parser, 3, '[', ']', '=')) {
- consume_tokens(parser, 3);
- operator->operator = Binary_Op_Subscript_Equals;
- goto operator_determined;
- }
-
- // The default case
- 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, Error_Critical, "Invalid binary operator.");
- } else {
- operator->operator = op;
- }
-
- operator_determined:
- operator->overload = parse_expression(parser, 0);
-
- ENTITY_SUBMIT(operator);
- return;
- }
- else if (parse_possible_directive(parser, "match") || parse_possible_directive(parser, "overload")) {
- AstDirectiveAddOverload *add_overload = make_node(AstDirectiveAddOverload, Ast_Kind_Directive_Add_Overload);
- add_overload->token = dir_token;
-
- 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;
- }
-
- parser->parse_calls = 0;
- add_overload->overloaded_function = (AstNode *) parse_expression(parser, 0);
- parser->parse_calls = 1;
-
- // Allow for
- // #match
- // something :: (....) {
- // }
- //
- // This will make converting something to a overloaded
- // function easier and require less copying by the programmer.
- if (next_tokens_are(parser, 2, ':', ':')) {
- consume_tokens(parser, 2);
- }
-
- add_overload->overload = parse_expression(parser, 0);
-
- ENTITY_SUBMIT(add_overload);
- return;
- }
- else if (parse_possible_directive(parser, "inject")) {
- AstInjection *inject = make_node(AstInjection, Ast_Kind_Injection);
- inject->token = dir_token;
-
- parser->parse_calls = 0;
- inject->full_loc = parse_expression(parser, 0);
- parser->parse_calls = 1;
-
- // See comment above
- if (next_tokens_are(parser, 2, ':', ':')) {
- consume_tokens(parser, 2);
- }
-
- inject->to_inject = parse_expression(parser, 0);
-
- ENTITY_SUBMIT(inject);
- return;
- }
- else if (parse_possible_directive(parser, "export")) {
- AstDirectiveExport *export = make_node(AstDirectiveExport, Ast_Kind_Directive_Export);
- export->token = dir_token;
- parser->parse_calls = 0;
- export->export_name_expr = parse_expression(parser, 0); // expect_token(parser, Token_Type_Literal_String);
- parser->parse_calls = 1;
-
- export->export = parse_expression(parser, 0);
-
- ENTITY_SUBMIT(export);
- return;
- }
- else if (parse_possible_directive(parser, "thread_local")) {
- OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
- AstMemRes* memres = parse_memory_reservation(parser, symbol, 1);
-
- binding = make_node(AstBinding, Ast_Kind_Binding);
- binding->token = symbol;
- binding->flags |= private_kind;
- binding->node = (AstNode *) memres;
-
- goto submit_binding_to_entities;
- }
- else if (parse_possible_directive(parser, "init")) {
- // :LinearTokenDependent
- parse_init_directive(parser, parser->curr - 2);
- return;
- }
- else if (parse_possible_directive(parser, "library")) {
- // :LinearTokenDependent
- AstDirectiveLibrary *library = make_node(AstDirectiveLibrary, Ast_Kind_Directive_Library);
- library->token = parser->curr - 2;
- library->library_symbol = parse_expression(parser, 0);
-
- ENTITY_SUBMIT(library);
- return;
- }
- else if (parse_possible_directive(parser, "tag")) {
- parser->tag_depth += 1;
-
- AstTyped *expr = parse_expression(parser, 0);
- bh_arr_push(parser->stored_tags, expr);
-
- parser->tag_depth -= 1;
- return;
- }
- else {
- OnyxToken* directive_token = expect_token(parser, '#');
- OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
-
- onyx_report_error(directive_token->pos, Error_Critical, "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, parser->curr->pos);
- }
-
- 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, package_node->token->pos);
-
- 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.current_symbol_stack = NULL;
- parser.current_function_stack = NULL;
- parser.scope_flags = NULL;
- parser.stored_tags = NULL;
- parser.parse_calls = 1;
- parser.tag_depth = 0;
-
- 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.current_symbol_stack, 4);
- bh_arr_new(global_heap_allocator, parser.scope_flags, 4);
- bh_arr_new(global_heap_allocator, parser.stored_tags, 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;
-
- parse_top_level_statements_until(parser, Token_Type_End_Stream);
-
- parser->current_scope = parser->current_scope->parent;
-}
+++ /dev/null
-
-//
-// Polymorphic Procedures
-//
-
-// This flag is used by some of the procedures that try working with polymorphic things,
-// but need to wait until more information is known. Instead of passing a out parameter
-// into each of these procedures, a single global variable is used instead. If the type
-// checker ever gets multi-threaded, this would have to become a threadlocal variable.
-static b32 flag_to_yield = 0;
-
-// This flag is used in the very special case that you are passing a polymorphic procedure
-// to a polymorphic procedure, and you have enough information to instantiate said procedure
-// in order to resolve the type of one of the return values.
-static b32 doing_nested_polymorph_lookup = 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 };
-AstTyped node_that_signals_failure = { Ast_Kind_Error, 0 };
-
-static void ensure_polyproc_cache_is_created(AstFunction* pp) {
- if (pp->concrete_funcs == NULL) sh_new_arena(pp->concrete_funcs);
- if (pp->active_queries.hashes == NULL) bh_imap_init(&pp->active_queries, global_heap_allocator, 31);
-}
-
-void insert_poly_sln_into_scope(Scope* scope, AstPolySolution *sln) {
- 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;
- ((AstTypeRawAlias *) node)->type = &basic_types[Basic_Kind_Type_Index];
- ((AstTypeRawAlias *) node)->type_id = sln->type->id;
- 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);
-}
-
-static void insert_poly_slns_into_scope(Scope* scope, bh_arr(AstPolySolution) slns) {
- bh_arr_each(AstPolySolution, sln, slns) {
- insert_poly_sln_into_scope(scope, sln);
- }
-}
-
-// 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) {
- solidified_func->func->flags |= Ast_Flag_Function_Used;
- solidified_func->func->flags |= Ast_Flag_From_Polymorphism;
-
- Entity func_header_entity = {
- .state = Entity_State_Resolve_Symbols,
- .type = Entity_Type_Function_Header,
- .function = solidified_func->func,
- .package = NULL,
- .scope = solidified_func->func->poly_scope,
- };
-
- Entity func_entity = {
- .state = Entity_State_Resolve_Symbols,
- .type = Entity_Type_Function,
- .function = solidified_func->func,
- .package = NULL,
- .scope = solidified_func->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_header_entity = entity_header;
- solidified_func->func->entity_header = entity_header;
- solidified_func->func->entity_body = entity_body;
- solidified_func->func->entity = 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(
- AstFunction* pp,
- bh_arr(AstPolySolution) slns,
- OnyxToken* tkn,
- b32 header_only) {
-
- AstSolidifiedFunction solidified_func;
- solidified_func.func_header_entity = NULL;
-
- // 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;
-
- if (header_only) {
- solidified_func.func = (AstFunction *) clone_function_header(context.ast_alloc, pp);
- solidified_func.func->flags |= Ast_Flag_Incomplete_Body;
-
- } else {
- solidified_func.func = (AstFunction *) ast_clone(context.ast_alloc, pp);
- }
-
- solidified_func.func->poly_scope = scope_create(context.ast_alloc, pp->parent_scope_of_poly_proc, poly_scope_pos);
- insert_poly_slns_into_scope(solidified_func.func->poly_scope, slns);
-
- 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(AstFunction* pp, AstSolidifiedFunction *solidified_func) {
- if (solidified_func->func->flags & Ast_Flag_Incomplete_Body) {
- clone_function_body(context.ast_alloc, solidified_func->func, pp);
-
- // 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));
-
- 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_Address_Of: {
- if (elem.actual->kind != Type_Kind_Pointer) break;
-
- bh_arr_push(elem_queue, ((PolySolveElem) {
- .type_expr = (AstType *) ((AstAddressOf *) elem.type_expr)->expr,
- .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 && elem.actual->kind != Type_Kind_DynArray
- && elem.actual->kind != Type_Kind_VarArgs && elem.actual->kind != Type_Kind_Array) break;
-
- bh_arr_push(elem_queue, ((PolySolveElem) {
- .type_expr = ((AstSliceType *) elem.type_expr)->elem,
- .kind = PSK_Type,
-
- // HACK: This makes the assumption that arrays, slices, dynamic arrays and varargs have the same element type at the same location.
- .actual = elem.actual->Slice.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.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_Call: {
- AstPolyCallType *pct = convert_call_to_polycall((AstCall *) elem.type_expr);
- elem.type_expr = (AstType *) pct;
-
- // fallthrough
- }
-
- 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(AstFunction* func, AstPolyParam* param, Arguments* args, char** err_msg) {
- bh_arr(AstTyped *) arg_arr = args->values;
- bh_arr(AstNamedValue *) named_values = args->named_values;
-
- if ((i32) param->idx < 0)
- return NULL;
-
- // 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 = 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;
- }
- }
-
- if (param->idx <= (u64) bh_arr_length(func->params)) {
- if (func->params[param->idx].default_value) {
- return (AstTyped *) func->params[param->idx].default_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;
-}
-
-static AstTyped* try_lookup_based_on_partial_function_type(AstFunction *pp, AstFunctionType *ft) {
- if (ft->partial_function_type == NULL) {
- AstType *old_return_type = ft->return_type;
- ft->return_type = (AstType *) &basic_type_void;
- ft->partial_function_type = type_build_from_ast(context.ast_alloc, (AstType *) ft);
- ft->return_type = old_return_type;
- if (!ft->partial_function_type) {
- doing_nested_polymorph_lookup = 1;
- return NULL;
- }
-
- assert(ft->partial_function_type);
- }
-
- AstTyped *result = (AstTyped *) polymorphic_proc_lookup(pp, PPLM_By_Function_Type, ft->partial_function_type, pp->token);
- if (result && result->type == NULL) {
- doing_nested_polymorph_lookup = 1;
- result = NULL;
- }
- if (result == &node_that_signals_a_yield) {
- doing_nested_polymorph_lookup = 1;
- result = NULL;
- }
-
- return result;
-}
-
-// 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, AstFunction* func, 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(func, param, args, err_msg);
- if (typed_param == NULL) return;
-
- // CLEANUP FIXME HACK TODO GROSS
- if (typed_param->kind == Ast_Kind_Argument) {
- AstTyped* potential = ((AstArgument *) typed_param)->value;
- if (potential->kind == Ast_Kind_Polymorphic_Proc) {
- if (param->idx < (u32) bh_arr_length(func->params)) {
- AstType *param_type = func->params[param->idx].local->type_node;
- if (param_type->kind == Ast_Kind_Function_Type) {
- AstFunctionType *ft = (AstFunctionType *) param_type;
- b32 all_types = 1;
- fori (i, 0, (i32) ft->param_count) {
- if (!node_is_type((AstNode *) ft->params[i])) {
- all_types = 0;
- break;
- }
- }
-
- if (all_types) {
- typed_param = try_lookup_based_on_partial_function_type((AstFunction *) potential, ft);
- }
- }
- }
- }
- }
-
- 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;
- }
-
- PolySolveResult res = solve_poly_type(param->poly_sym, param->type_expr, actual_type);
- if (res.kind == PSK_Undefined) {
- *err_msg = bh_aprintf(global_scratch_allocator,
- "Unable to solve for polymorphic variable '%b', given the type '%s'.",
- param->poly_sym->token->text,
- param->poly_sym->token->length,
- type_get_name(actual_type));
- }
-
- *resolved = res;
-}
-
-
-// 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. In other words, this handles the ($Baked: type) case.
-// 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, AstFunction* func, 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(func, 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;
- }
-
- Type* param_type = NULL;
- AstType *param_type_expr = func->params[param->idx].local->type_node;
- 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;
- }
-
- param_type = type_build_from_ast(context.ast_alloc, param_type_expr);
- if (param_type == NULL) {
- flag_to_yield = 1;
- *err_msg = "Waiting to know type for polymorphic value.";
- return;
- }
-
- AstTyped* value_to_use = value;
- if (value->kind == Ast_Kind_Macro) {
- value_to_use = (AstTyped *) get_function_from_node((AstNode *) value);
- }
-
- TypeMatch tm = unify_node_and_type(&value_to_use, param_type);
- if (tm == TYPE_MATCH_FAILED) {
- 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(func),
- type_get_name(param_type),
- param->idx + 1,
- bh_num_suffix(param->idx + 1),
- node_get_type_name(value_to_use));
- return;
- }
-
- if (tm == TYPE_MATCH_YIELD) flag_to_yield = 1;
-
- *resolved = ((PolySolveResult) { PSK_Value, value });
- }
-
- if (orig_value->kind == Ast_Kind_Argument) {
- ((AstArgument *) orig_value)->is_baked = 1;
- }
-}
-
-TypeMatch find_polymorphic_sln(AstPolySolution *out, AstPolyParam *param, AstFunction *func, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
- // 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, func, param, pp_lookup, actual, err_msg); break;
- case PPK_Baked_Value: solve_for_polymorphic_param_value(&resolved, func, param, pp_lookup, actual, err_msg); break;
-
- default: if (err_msg) *err_msg = "Invalid polymorphic parameter kind. This is a compiler bug.";
- }
-
- if (doing_nested_polymorph_lookup) {
- doing_nested_polymorph_lookup = 0;
- return TYPE_MATCH_SPECIAL;
- }
-
- if (flag_to_yield) {
- flag_to_yield = 0;
- return TYPE_MATCH_YIELD;
- }
-
- switch (resolved.kind) {
- case PSK_Type:
- out->kind = PSK_Type;
- out->poly_sym = param->poly_sym;
- out->type = resolved.actual;
- return TYPE_MATCH_SUCCESS;
-
- case PSK_Value:
- out->kind = PSK_Value;
- out->poly_sym = param->poly_sym;
- out->value = resolved.value;
- return TYPE_MATCH_SUCCESS;
-
- case PSK_Undefined:
- default:
- // 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);
-
- out->kind = PSK_Undefined;
- return TYPE_MATCH_FAILED;
- }
-}
-
-// 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(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken *tkn, b32 necessary) {
- ensure_polyproc_cache_is_created(pp);
- if (bh_imap_has(&pp->active_queries, (u64) actual)) {
- AstPolyQuery *query = (AstPolyQuery *) bh_imap_get(&pp->active_queries, (u64) actual);
- assert(query->kind == Ast_Kind_Polymorph_Query);
- assert(query->entity);
-
- if (query->entity->state == Entity_State_Finalized) return query->slns;
- if (query->entity->state == Entity_State_Failed) return NULL;
-
- flag_to_yield = 1;
- return NULL;
- }
-
- 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);
-
- AstPolyQuery *query = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyQuery), Ast_Kind_Polymorph_Query);
- query->token = pp->token;
- query->proc = pp;
- query->pp_lookup = pp_lookup;
- query->given = actual;
- query->error_loc = tkn;
- query->slns = slns;
- query->function_header = clone_function_header(context.ast_alloc, pp);
- query->function_header->flags |= Ast_Flag_Header_Check_No_Error;
- query->function_header->scope = NULL;
- query->error_on_fail = necessary;
- query->successful_symres = 1;
-
- bh_imap_put(&pp->active_queries, (u64) actual, (u64) query);
- add_entities_for_node(NULL, (AstNode *) query, NULL, NULL);
-
- flag_to_yield = 1;
- 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(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn) {
- ensure_polyproc_cache_is_created(pp);
-
- bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, tkn, 1);
- if (slns == NULL) {
- if (flag_to_yield) {
- flag_to_yield = 0;
- return (AstFunction *) &node_that_signals_a_yield;
- }
-
- return NULL;
- }
-
- AstFunction* result = polymorphic_proc_solidify(pp, slns, tkn);
- return result;
-}
-
-AstFunction* polymorphic_proc_solidify(AstFunction* 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);
- i32 index = shgeti(pp->concrete_funcs, unique_key);
- if (index != -1) {
- AstSolidifiedFunction solidified_func = pp->concrete_funcs[index].value;
-
- // 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);
- add_solidified_function_entities(&solidified_func);
-
- // NOTE: Cache the function for later use, reducing duplicate functions.
- shput(pp->concrete_funcs, unique_key, solidified_func);
-
- return (AstFunction *) &node_that_signals_a_yield;
-}
-
-// NOTE: This can return either a AstFunction or an AstFunction, depending if enough parameters were
-// supplied to remove all the polymorphic variables from the function.
-AstNode* polymorphic_proc_try_solidify(AstFunction* 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 {
- if (pp->name) {
- onyx_report_error(tkn->pos, Error_Critical, "'%b' is not a type variable of '%s'.",
- sln->poly_sym->token->text, sln->poly_sym->token->length, pp->name);
- } else {
- onyx_report_error(tkn->pos, Error_Critical, "'%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
- AstFunction* new_pp = onyx_ast_node_new(context.ast_alloc, sizeof(AstFunction), Ast_Kind_Polymorphic_Proc);
- memcpy(new_pp, pp, sizeof(AstFunction));
- new_pp->token = tkn;
- new_pp->poly_params = bh_arr_copy(context.ast_alloc, 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(AstFunction* pp, PolyProcLookupMethod pp_lookup, ptr actual) {
- ensure_polyproc_cache_is_created(pp);
- bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, NULL, 0);
- if (flag_to_yield) {
- flag_to_yield = 0;
- return (AstFunction *) &node_that_signals_a_yield;
- }
- if (slns == NULL) return NULL;
-
- ensure_polyproc_cache_is_created(pp);
-
- return polymorphic_proc_build_only_header_with_slns(pp, slns, 0);
-}
-
-AstFunction* polymorphic_proc_build_only_header_with_slns(AstFunction* pp, bh_arr(AstPolySolution) slns, b32 error_if_failed) {
- AstSolidifiedFunction solidified_func;
-
- char* unique_key = build_poly_slns_unique_key(slns);
- i32 index = shgeti(pp->concrete_funcs, unique_key);
- if (index != -1) {
- solidified_func = pp->concrete_funcs[index].value;
-
- } 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.func_header_entity) {
- if (solidified_func.func_header_entity->state == Entity_State_Finalized) return solidified_func.func;
- if (solidified_func.func_header_entity->state == Entity_State_Failed) return NULL;
-
- return (AstFunction *) &node_that_signals_a_yield;
- }
-
- BH_MASK_SET(solidified_func.func->flags, !error_if_failed, Ast_Flag_Header_Check_No_Error);
-
- Entity func_header_entity = {
- .state = Entity_State_Resolve_Symbols,
- .type = Entity_Type_Temp_Function_Header,
- .function = solidified_func.func,
- .package = NULL,
- .scope = solidified_func.func->poly_scope,
- };
-
- Entity* func_header_entity_ptr = entity_heap_insert(&context.entities, func_header_entity);
- solidified_func.func_header_entity = func_header_entity_ptr;
-
- // NOTE: Cache the function for later use.
- shput(pp->concrete_funcs, unique_key, solidified_func);
-
- return (AstFunction *) &node_that_signals_a_yield;
-}
-
-typedef struct AutoPolymorphVariable {
- u32 idx;
- u32 variable_count;
- AstType *base_type;
- AstType **replace;
-} AutoPolymorphVariable;
-
-// This should be called after all the parameter types have been symresed, but before anything
-// happens to the body.
-b32 potentially_convert_function_to_polyproc(AstFunction *func) {
- bh_arr(AutoPolymorphVariable) auto_vars = NULL;
- bh_arr_new(global_heap_allocator, auto_vars, 2);
-
- u32 param_idx = 0;
- bh_arr_each(AstParam, param, func->params) {
- AstType **to_replace = ¶m->local->type_node;
- AstType *param_type = param->local->type_node;
-
- b32 done = 0;
- while (!done && param_type) {
- switch (param_type->kind) {
- case Ast_Kind_Pointer_Type: to_replace = &((AstPointerType *) *to_replace)->elem; param_type = ((AstPointerType *) param_type)->elem; break;
- case Ast_Kind_Array_Type: to_replace = &((AstArrayType *) *to_replace)->elem; param_type = ((AstArrayType *) param_type)->elem; break;
- case Ast_Kind_Slice_Type: to_replace = &((AstSliceType *) *to_replace)->elem; param_type = ((AstSliceType *) param_type)->elem; break;
- case Ast_Kind_DynArr_Type: to_replace = &((AstDynArrType *) *to_replace)->elem; param_type = ((AstDynArrType *) param_type)->elem; break;
- case Ast_Kind_Alias: param_type = (AstType *) ((AstAlias *) param_type)->alias; break;
- case Ast_Kind_Type_Alias: param_type = ((AstTypeAlias *) param_type)->to; break;
- case Ast_Kind_Poly_Struct_Type: {
- AutoPolymorphVariable apv;
- apv.idx = param_idx;
- apv.base_type = param->local->type_node;
- apv.variable_count = bh_arr_length(((AstPolyStructType *) param_type)->poly_params);
- apv.replace = to_replace;
-
- bh_arr_push(auto_vars, apv);
- done = 1;
- break;
- }
-
- default: done = 1; break;
- }
- }
-
- param_idx++;
- }
-
- if (bh_arr_length(auto_vars) == 0) return 0;
-
- param_idx = 0;
- bh_arr_each(AutoPolymorphVariable, apv, auto_vars) {
- AstPolyParam pp;
- pp.idx = apv->idx;
- pp.kind = PPK_Poly_Type;
-
- AstPolyCallType* pcall = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyCallType), Ast_Kind_Poly_Call_Type);
- pcall->callee = *apv->replace;
- pcall->token = pcall->callee->token;
- bh_arr_new(global_heap_allocator, pcall->params, apv->variable_count);
-
- if (apv->base_type->kind == Ast_Kind_Poly_Struct_Type) {
- pp.type_expr = (AstType *) pcall;
- } else {
- pp.type_expr = apv->base_type;
- }
- *apv->replace = (AstType *) pcall;
-
- fori (i, 0, apv->variable_count) {
- OnyxToken* name_token = bh_alloc_item(context.ast_alloc, OnyxToken);
- name_token->text = bh_aprintf(context.ast_alloc, "__autopoly_var_%d\0", param_idx);
- name_token->length = strlen(name_token->text);
- name_token->type = Token_Type_Symbol;
- name_token->pos = pcall->token->pos;
-
- pp.poly_sym = make_symbol(context.ast_alloc, name_token);
- bh_arr_push(pcall->params, pp.poly_sym);
- bh_arr_push(func->poly_params, pp);
- param_idx ++;
- }
- }
-
- convert_function_to_polyproc(func);
-
- bh_arr_each(AstParam, param, func->params) {
- param->local->flags |= Ast_Flag_Param_Symbol_Dirty;
- }
-
- return 1;
-}
-
-//
-// Polymorphic Structures
-//
-//
-// Currently, I am not very happy about how polymorphic structure generation works. My biggest problem
-// with it is that it is very different from the polymorhic procedure generation. Also, it needs to
-// completely generate and check the structure right away, which means there is a lot of up-front work
-// done here that could probably be done elsewhere. This really relates to a large problem in the compiler
-// that types need to be known completely by the time symbol resolution is done, even though that
-// information shouldn't need to be known until right before the types are checked.
-//
-// The above documentation is very incorrect but I don't want to fix it right now. Basically, polymorphic
-// structures now have a delay instantiation phase and are not forced to be completed immediately.
-
-char* build_poly_struct_name(AstPolyStructType* ps_type, Type* cs_type) {
- char name_buf[256];
- fori (i, 0, 256) name_buf[i] = 0;
-
- strncat(name_buf, ps_type->name, 255);
- strncat(name_buf, "(", 255);
- bh_arr_each(AstPolySolution, ptype, cs_type->Struct.poly_sln) {
- if (ptype != cs_type->Struct.poly_sln)
- strncat(name_buf, ", ", 255);
-
- // This logic will have to be other places as well.
-
- switch (ptype->kind) {
- case PSK_Undefined: assert(0); break;
- case PSK_Type: strncat(name_buf, type_get_name(ptype->type), 255); break;
- case PSK_Value: {
- // FIX
- AstNode* value = strip_aliases((AstNode *) ptype->value);
-
- if (value->kind == Ast_Kind_NumLit) {
- AstNumLit* nl = (AstNumLit *) value;
- if (type_is_integer(nl->type)) {
- strncat(name_buf, bh_bprintf("%l", nl->value.l), 127);
- } else {
- strncat(name_buf, "numlit (FIX ME)", 127);
- }
- } else if (value->kind == Ast_Kind_Code_Block) {
- AstCodeBlock* code = (AstCodeBlock *) value;
- OnyxFilePos code_loc = code->token->pos;
- strncat(name_buf, bh_bprintf("code at %s:%d,%d", code_loc.filename, code_loc.line, code_loc.column), 127);
- } else {
- strncat(name_buf, "<expr>", 127);
- }
-
- break;
- }
- }
- }
- strncat(name_buf, ")", 255);
-
- return bh_aprintf(global_heap_allocator, "%s", name_buf);
-}
-
-Type* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos, b32 error_if_failed) {
- // @Cleanup
- assert(ps_type->scope != NULL);
-
- if (ps_type->concrete_structs == NULL) {
- sh_new_arena(ps_type->concrete_structs);
- }
-
- if (bh_arr_length(slns) != bh_arr_length(ps_type->poly_params)) {
- onyx_report_error(pos, Error_Critical, "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];
- i++;
- }
-
- char* unique_key = build_poly_slns_unique_key(slns);
- i32 index = shgeti(ps_type->concrete_structs, unique_key);
- if (index != -1) {
- AstStructType* concrete_struct = ps_type->concrete_structs[index].value;
-
- if (concrete_struct->entity_type->state < Entity_State_Check_Types) {
- return NULL;
- }
-
- if (concrete_struct->entity_type->state == Entity_State_Failed) {
- return (Type *) &node_that_signals_failure;
- }
-
- 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 cs_type;
- }
-
- 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);
- concrete_struct->polymorphic_error_loc = pos;
- BH_MASK_SET(concrete_struct->flags, !error_if_failed, Ast_Flag_Header_Check_No_Error);
-
-
- i64 arg_count = bh_arr_length(ps_type->poly_params);
- bh_arr_new(global_heap_allocator, concrete_struct->polymorphic_argument_types, arg_count);
- bh_arr_set_length(concrete_struct->polymorphic_argument_types, arg_count);
- concrete_struct->polymorphic_arguments = bh_arr_copy(global_heap_allocator, slns);
-
- fori (i, 0, (i64) bh_arr_length(ps_type->poly_params)) {
- concrete_struct->polymorphic_argument_types[i] = (AstType *) ast_clone(context.ast_alloc, ps_type->poly_params[i].type_node);
- }
-
- shput(ps_type->concrete_structs, unique_key, concrete_struct);
- add_entities_for_node(NULL, (AstNode *) concrete_struct, sln_scope, NULL);
- return NULL;
-}
+++ /dev/null
-#define BH_DEBUG
-#include "parser.h"
-#include "utils.h"
-#include "astnodes.h"
-#include "errors.h"
-
-// :EliminatingSymres - notes the places where too much work is being done in symbol resolution
-
-// Variables used during the symbol resolution phase.
-static Scope* curr_scope = NULL;
-static b32 report_unresolved_symbols = 1;
-static b32 resolved_a_symbol = 0;
-
-// 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)
-
-#define SYMRES_INVISIBLE(kind, node, ...) do { \
- (node)->flags |= Ast_Flag_Symbol_Invisible; \
- SymresStatus ss = symres_ ## kind (__VA_ARGS__); \
- (node)->flags &= ~Ast_Flag_Symbol_Invisible; \
- if (ss > Symres_Errors_Start) return ss; \
- } while (0)
-
-typedef enum SymresStatus {
- Symres_Success,
- Symres_Complete,
- Symres_Goto_Parse,
-
- 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** pcall);
-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_case(AstSwitchCase *casenode);
-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 SymresStatus symres_constraint(AstConstraint* constraint);
-static SymresStatus symres_polyquery(AstPolyQuery *query);
-
-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) {
- token_toggle_end(token);
- char *closest = find_closest_symbol_in_scope_and_parents(curr_scope, token->text);
- token_toggle_end(token);
-
- if (closest) onyx_report_error(token->pos, Error_Critical, "Unable to resolve symbol '%b'. Did you mean '%s'?", token->text, token->length, closest);
- else onyx_report_error(token->pos, Error_Critical, "Unable to resolve symbol '%b'.", token->text, token->length);
-
- return Symres_Error;
- } else {
- return Symres_Yield_Macro;
- }
-
- } else {
- *symbol_node = res;
- resolved_a_symbol = 1;
- }
-
- 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;
- s_node->flags |= Ast_Flag_Comptime;
-
- if (s_node->scope) {
- // FIX: This is probably wrong for the long term.
- s_node->scope->parent = curr_scope;
-
- scope_enter(s_node->scope);
- }
-
- if (s_node->min_size_) SYMRES(expression, &s_node->min_size_);
- if (s_node->min_alignment_) SYMRES(expression, &s_node->min_alignment_);
-
- if (s_node->polymorphic_argument_types) {
- assert(s_node->polymorphic_arguments);
-
- SymresStatus ss = Symres_Success, result;
- fori (i, 0, (i64) bh_arr_length(s_node->polymorphic_argument_types)) {
- result = symres_type(&s_node->polymorphic_argument_types[i]);
- if (result > ss) ss = result;
-
- if (s_node->polymorphic_arguments[i].value) {
- result = symres_expression(&s_node->polymorphic_arguments[i].value);
- if (result > ss) ss = result;
- }
- }
- }
-
- if (s_node->constraints.constraints) {
- bh_arr_each(AstConstraint *, constraint, s_node->constraints.constraints) {
- SYMRES(constraint, *constraint);
- }
- }
-
- 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 (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);
- 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;
-
- if (ftype->param_count > 0) {
- fori (i, 0, (i64) ftype->param_count) {
- SYMRES(type, &ftype->params[i]);
- }
- }
-
- SYMRES(type, &ftype->return_type);
- 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;
-
- if (pst_node->scope == NULL) {
- pst_node->scope = scope_create(context.ast_alloc, pst_node->entity->scope, pst_node->token->pos);
- }
- 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_INVISIBLE(type, alias, (AstType **) &alias->alias);
-
- break;
- }
-
- case Ast_Kind_Typeof: {
- AstTypeOf* type_of = (AstTypeOf *) *type;
- SYMRES(expression, &type_of->expr);
- break;
- }
-
- case Ast_Kind_Distinct_Type: {
- AstDistinctType *distinct = (AstDistinctType *) *type;
- SYMRES(type, &distinct->base_type);
- 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** pcall) {
- AstCall *call = *pcall;
- SYMRES(expression, (AstTyped **) &call->callee);
- SYMRES(arguments, &call->args);
-
- AstNode* callee = strip_aliases((AstNode *) call->callee);
- if (callee->kind == Ast_Kind_Poly_Struct_Type) {
- *pcall = (AstCall *) convert_call_to_polycall(call);
- SYMRES(type, (AstType **) pcall);
- return Symres_Success;
- }
-
- 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);
-
- b32 force_a_lookup = 0;
-
- if (expr->kind == Ast_Kind_Enum_Type || expr->kind == Ast_Kind_Type_Raw_Alias) {
- force_a_lookup = 1;
- }
-
- //
- // If we are trying to access a field on an alias, we have to make sure
- // the alias is "ready" to have a symbol looked up inside of it. This means
- // the alias should have passed symbol resolution. If not, force a lookup
- // and yield if the alias was not ready.
- if ((*fa)->expr->kind == Ast_Kind_Alias) {
- assert((*fa)->expr->entity);
- if ((*fa)->expr->entity->state < Entity_State_Check_Types) {
- force_a_lookup = 1;
- }
- }
-
- 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) {
- token_toggle_end((*fa)->token);
- char *closest = find_closest_symbol_in_node((AstNode *) expr, (*fa)->token->text);
- token_toggle_end((*fa)->token);
-
- AstPackage *package = (AstPackage *) strip_aliases((AstNode *) (*fa)->expr);
-
- if (closest) {
- onyx_report_error((*fa)->token->pos, Error_Critical, "'%b' was not found in package '%s'. Did you mean '%s'?",
- (*fa)->token->text,
- (*fa)->token->length,
- package->package->name,
- closest);
- } else {
- onyx_report_error((*fa)->token->pos, Error_Critical, "'%b' was not found in package '%s'. Perhaps it is defined in a file that wasn't loaded?",
- (*fa)->token->text,
- (*fa)->token->length,
- package->package->name);
- }
- return Symres_Error;
-
- } else {
- return Symres_Yield_Macro;
- }
- }
- else if (force_a_lookup) {
- if (context.cycle_detected) {
- onyx_report_error((*fa)->token->pos, Error_Critical, "'%b' does not exist here. This is a bad error message.",
- (*fa)->token->text,
- (*fa)->token->length);
- return Symres_Error;
- }
-
- 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, Error_Critical, "Pipe operator expected call on right side.");
- return Symres_Error;
- }
-
- if ((*pipe)->left == NULL) return Symres_Error;
-
- // :EliminatingSymres
- 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) {
- SYMRES(expression, &(*mcall)->left);
- if ((*mcall)->left == NULL) return Symres_Error;
-
- // :EliminatingSymres
- if (((*mcall)->flags & Ast_Flag_Has_Been_Symres) == 0) {
- if ((*mcall)->right->kind != Ast_Kind_Call) {
- onyx_report_error((*mcall)->token->pos, Error_Critical, "'->' expected procedure call on right side.");
- return Symres_Error;
- }
-
- AstFieldAccess* implicit_field_access = make_field_access(context.ast_alloc, (*mcall)->left, NULL);
- implicit_field_access->token = ((AstCall *) (*mcall)->right)->callee->token;
- ((AstCall *) (*mcall)->right)->callee = (AstTyped *) implicit_field_access;
- (*mcall)->flags |= Ast_Flag_Has_Been_Symres;
- }
-
- assert((*mcall)->right->kind == Ast_Kind_Call);
- SYMRES(expression, (AstTyped **) &(*mcall)->right);
-
- 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);
-
- // :EliminatingSymres
- 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);
-
- // :EliminatingSymres
- 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_address_of(AstAddressOf** paof) {
- AstAddressOf *aof = (AstAddressOf *) *paof;
- SYMRES(expression, &aof->expr);
-
- AstTyped *expr = (AstTyped *) strip_aliases((AstNode *) aof->expr);
- if (node_is_type((AstNode *) expr)) {
- AstPointerType *pt = onyx_ast_node_new(context.ast_alloc, sizeof(AstPointerType), Ast_Kind_Pointer_Type);
- pt->token = aof->token;
- pt->elem = (AstType *) expr;
- pt->__unused = aof->next;
- *paof = (AstAddressOf *) pt;
- SYMRES(type, (AstType **) &pt);
- return Symres_Success;
- }
-
- 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_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_Address_Of: SYMRES(address_of, (AstAddressOf **) expr); break;
- case Ast_Kind_Alias: {
- AstAlias *alias = (AstAlias *) *expr;
- SYMRES_INVISIBLE(expression, alias, &alias->alias);
- break;
- }
-
- case Ast_Kind_Range_Literal:
- SYMRES(expression, &((AstRangeLiteral *)(*expr))->low);
- SYMRES(expression, &((AstRangeLiteral *)(*expr))->high);
-
- // :EliminatingSymres
- SYMRES(type, &builtin_range_type);
- (*expr)->type_node = builtin_range_type;
- break;
-
- case Ast_Kind_Function:
- case Ast_Kind_NumLit:
- SYMRES(type, &(*expr)->type_node);
- break;
-
- case Ast_Kind_StrLit: {
- AstStrLit* str = (AstStrLit *) *expr;
- if (str->is_cstr) {
- SYMRES(type, &builtin_cstring_type);
- str->type_node = builtin_cstring_type;
-
- } else {
- SYMRES(type, &builtin_string_type);
- str->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;
-
- case Ast_Kind_Param:
- if ((*expr)->flags & Ast_Flag_Param_Symbol_Dirty) {
- assert((*expr)->token->type == Token_Type_Symbol);
- *expr = (AstTyped *) make_symbol(context.ast_alloc, (*expr)->token);
- SYMRES(expression, expr);
- }
- 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) {
- if (context.cycle_detected) {
- onyx_report_error(ifnode->token->pos, Error_Waiting_On, "Waiting on static if resolution.");
- return Symres_Error;
- } else {
- 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_case(AstSwitchCase *casenode) {
- if (!casenode->is_default) {
- bh_arr_each(AstTyped *, expr, casenode->values) {
- SYMRES(expression, expr);
- }
- }
-
- SYMRES(block, casenode->block);
- 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);
-
- if (switchnode->cases == NULL) {
- SYMRES(block, switchnode->case_block);
- } else {
- bh_arr_each(AstSwitchCase *, pcase, switchnode->cases) {
- SYMRES(case, *pcase);
- }
-
- if (switchnode->default_case) SYMRES(block, switchnode->default_case);
- }
-
- if (switchnode->switch_kind == Switch_Kind_Use_Equals && switchnode->case_exprs) {
- bh_arr_each(CaseToBlock, ctb, switchnode->case_exprs) {
- SYMRES(expression, (AstTyped **) &ctb->comparison);
- }
- }
-
- 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);
-
- AstTyped *use_expr = (AstTyped *) strip_aliases((AstNode *) use->expr);
-
- // :EliminatingSymres
- 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, Error_Critical,
- "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);
- }
- }
-
- if (!use->entity) {
- add_entities_for_node(NULL, (AstNode *) use, curr_scope, NULL);
- }
-
- package_track_use_package(package->package, use->entity);
- return Symres_Success;
- }
-
- if (use_expr->kind == Ast_Kind_Foreign_Block) {
- AstForeignBlock* fb = (AstForeignBlock *) use_expr;
- if (fb->entity->state <= Entity_State_Resolve_Symbols) return Symres_Yield_Macro;
-
- if (fb->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, fb->scope, pos);
-
- } else {
- bh_arr_each(QualifiedUse, qu, use->only) {
- AstNode* thing = symbol_resolve(fb->scope, qu->symbol_name);
- if (thing == NULL) { // :SymresStall
- if (report_unresolved_symbols) {
- onyx_report_error(qu->symbol_name->pos, Error_Critical,
- "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);
- }
- }
-
- 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, Error_Critical,
- "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;
-
- // :EliminatingSymres
- 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;
-
- fori (i, 0, shlen(st->Struct.members)) {
- StructMember* value = st->Struct.members[i].value;
- AstFieldAccess* fa = make_field_access(context.ast_alloc, use_expr, value->name);
- symbol_raw_introduce(curr_scope, value->name, use->token->pos, (AstNode *) fa);
- }
-
- return Symres_Success;
- }
-
-cannot_use:
- onyx_report_error(use->token->pos, Error_Critical, "Cannot use this because its type is unknown.");
- return Symres_Error;
-}
-
-static SymresStatus symres_directive_solidify(AstDirectiveSolidify** psolid) {
- AstDirectiveSolidify* solid = *psolid;
-
- SYMRES(expression, (AstTyped **) &solid->poly_proc);
- if (solid->poly_proc && solid->poly_proc->kind == Ast_Kind_Directive_Solidify) {
- AstFunction* potentially_resolved_proc = (AstFunction *) ((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, Error_Critical, "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);
- }
-
- return Symres_Success;
-}
-
-static SymresStatus symres_directive_defined(AstDirectiveDefined** pdefined) {
- AstDirectiveDefined* defined = *pdefined;
-
- b32 has_to_be_resolved = context.cycle_almost_detected;
-
- resolved_a_symbol = 0;
- SymresStatus ss = symres_expression(&defined->expr);
- if (has_to_be_resolved && ss != Symres_Success && !resolved_a_symbol) {
- // The symbol definitely was not found and there is no chance that it could be found.
- defined->is_defined = 0;
- return Symres_Success;
- }
-
- if (ss == Symres_Success) {
- defined->is_defined = 1;
- return Symres_Success;
- }
-
- return Symres_Yield_Macro;
-}
-
-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_Switch_Case: SYMRES(case, (AstSwitchCase *) *stmt); break;
- case Ast_Kind_Jump: break;
- case Ast_Kind_Directive_Remove: 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) {
- AstNode** start = &block->body;
- fori (i, 0, block->statement_idx) {
- start = &(*start)->next;
- }
-
- b32 remove = 0;
-
- while (*start) {
- SymresStatus cs = symres_statement(start, &remove);
-
- if (remove) {
- remove = 0;
- AstNode* tmp = (*start)->next;
- (*start)->next = NULL;
- (*start) = tmp;
-
- } else {
- switch (cs) {
- case Symres_Success:
- start = &(*start)->next;
- block->statement_idx++;
- break;
-
- default:
- return cs;
- }
- }
- }
-
- block->statement_idx = 0;
- }
-
- 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);
-
- if (func->constraints.constraints != NULL && func->constraints.constraints_met == 0) {
- bh_arr_each(AstConstraint *, constraint, func->constraints.constraints) {
- SYMRES(constraint, *constraint);
- }
-
- // Return early here to finish checking constraints in the checker.
- // Will resume here after constraints have been met.
- return Symres_Success;
- }
-
- 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;
- }
- }
-
- bh_arr_each(AstParam, param, func->params) {
- symbol_introduce(curr_scope, param->local->token, (AstNode *) param->local);
- }
-
- bh_arr_each(AstParam, param, func->params) {
- if (param->local->type_node != NULL) {
- SYMRES_INVISIBLE(type, param->local, ¶m->local->type_node);
- }
- }
-
- if (potentially_convert_function_to_polyproc(func)) {
- return Symres_Complete;
- }
-
- if (func->nodes_that_need_entities_after_clone && bh_arr_length(func->nodes_that_need_entities_after_clone) > 0 && func->entity) {
- bh_arr_each(AstNode *, node, func->nodes_that_need_entities_after_clone) {
- // This makes a lot of assumptions about how these nodes are being processed,
- // and I don't want to start using this with other nodes without considering
- // what the ramifications of that is.
- assert((*node)->kind == Ast_Kind_Static_If || (*node)->kind == Ast_Kind_File_Contents);
-
- // Need to curr_scope->parent because curr_scope is the function body scope.
- Scope *scope = curr_scope->parent;
-
- if ((*node)->kind == Ast_Kind_Static_If) {
- AstIf *static_if = (AstIf *) *node;
- assert(static_if->defined_in_scope);
- scope = static_if->defined_in_scope;
-
- if (func->poly_scope) {
- scope = scope_create(context.ast_alloc, scope, static_if->token->pos);
- scope_include(scope, func->poly_scope, static_if->token->pos);
- }
- }
-
- add_entities_for_node(NULL, *node, scope, func->entity->package);
- }
-
- bh_arr_set_length(func->nodes_that_need_entities_after_clone, 0);
- }
-
- SYMRES(type, &func->return_type);
-
- scope_leave();
-
- return Symres_Success;
-}
-
-SymresStatus symres_function(AstFunction* func) {
- if (func->entity_header && func->entity_header->state < Entity_State_Check_Types) return Symres_Yield_Macro;
- if (func->kind == Ast_Kind_Polymorphic_Proc) return Symres_Complete;
- assert(func->scope);
-
- scope_enter(func->scope);
-
- if ((func->flags & Ast_Flag_Has_Been_Symres) == 0) {
- // :EliminatingSymres
- 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->is_used && !param->use_processed) {
- 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) 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;
- }
-
- if (st->Struct.status != SPS_Uses_Done) return Symres_Yield_Macro;
-
- fori (i, 0, shlen(st->Struct.members)) {
- StructMember* value = st->Struct.members[i].value;
- 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);
- }
-
- param->use_processed = 1;
-
- } else if (param->local->type != NULL) {
- onyx_report_error(param->local->token->pos, Error_Critical, "Can only 'use' structures or pointers to structures.");
-
- } else {
- // :ExplicitTyping
- onyx_report_error(param->local->token->pos, Error_Critical, "Cannot deduce type of parameter '%b'; Try adding it explicitly.",
- param->local->token->text,
- param->local->token->length);
- }
- }
- }
-
- bh_arr_each(AstTyped *, pexpr, func->tags) {
- SYMRES(expression, pexpr);
- }
-
- 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, Error_Critical,
- "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;
-
- if (enum_node->scope == NULL) {
- enum_node->backing_type = type_build_from_ast(context.ast_alloc, enum_node->backing);
- enum_node->scope = scope_create(context.ast_alloc, curr_scope, enum_node->token->pos);
-
- type_build_from_ast(context.ast_alloc, (AstType *) enum_node);
- }
-
- scope_enter(enum_node->scope);
-
- // :EliminatingSymres
- u64 next_assign_value = enum_node->is_flags ? 1 : 0;
- bh_arr_each(AstEnumValue *, value, enum_node->values) {
- if ((*value)->flags & Ast_Flag_Has_Been_Checked) continue;
-
- (*value)->type = enum_node->etcache;
- (*value)->flags |= Ast_Flag_Comptime;
-
- if ((*value)->value != NULL) {
- SYMRES(expression, &(*value)->value);
-
- if ((*value)->value->kind == Ast_Kind_Enum_Value) {
- (*value)->value = ((AstEnumValue *) (*value)->value)->value;
- (*value)->value->type = enum_node->etcache;
- }
-
- if ((*value)->value->kind == Ast_Kind_NumLit) {
- AstNumLit *n_value = (AstNumLit *) (*value)->value;
- resolve_expression_type((AstTyped *) n_value);
-
- if (type_is_small_integer(n_value->type)) {
- next_assign_value = n_value->value.i;
- } else if (type_is_integer(n_value->type)) {
- next_assign_value = n_value->value.l;
- } else {
- onyx_report_error((*value)->token->pos, Error_Critical, "expected numeric integer literal for enum initialization, got '%s'", type_get_name(n_value->type));
- return Symres_Error;
- }
-
- n_value->type = enum_node->etcache;
-
- } else {
- if ((*value)->entity == NULL) {
- add_entities_for_node(NULL, (AstNode *) (*value), enum_node->scope, NULL);
- }
-
- if (context.cycle_detected) {
- onyx_report_error((*value)->token->pos, Error_Critical, "Expected compile time known value for enum initialization.");
- return Symres_Error;
- }
-
- return Symres_Yield_Macro;
- }
-
- } else {
- AstNumLit* num = make_int_literal(context.ast_alloc, next_assign_value);
- num->type = enum_node->etcache;
-
- (*value)->value = (AstTyped *) num;
- }
-
- symbol_introduce(enum_node->scope, (*value)->token, (AstNode *) (*value));
-
- (*value)->flags |= Ast_Flag_Comptime | Ast_Flag_Has_Been_Checked;
-
- if (enum_node->is_flags) {
- next_assign_value <<= 1;
- } else {
- next_assign_value++;
- }
- }
-
- scope_leave();
-
- // HACK this ensure that you can only lookup symbols in an Enum that are actually defined in the enum.
- // However, during the symbol resolution of the values in an enum, they need to be able to see the
- // enclosing scope.
- enum_node->scope->parent = NULL;
-
- 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);
-
- if (st->meta_tags) {
- bh_arr_each(AstTyped *, meta, st->meta_tags) {
- SYMRES(expression, meta);
- }
- }
-
- bh_arr_each(AstStructMember *, smem, st->members) {
- if ((*smem)->initial_value != NULL) {
- SYMRES(expression, &(*smem)->initial_value);
- }
-
- if ((*smem)->meta_tags != NULL) {
- bh_arr_each(AstTyped *, meta, (*smem)->meta_tags) {
- SYMRES(expression, meta);
- }
- }
- }
-
- if (st->scope) scope_leave();
- return Symres_Success;
-}
-
-static SymresStatus symres_polyproc(AstFunction* pp) {
- pp->flags |= Ast_Flag_Comptime;
- pp->parent_scope_of_poly_proc = curr_scope;
- return Symres_Success;
-}
-
-static SymresStatus symres_static_if(AstIf* static_if) {
- if (static_if->flags & Ast_Flag_Dead) return Symres_Complete;
-
- SYMRES(expression, &static_if->cond);
- return Symres_Success;
-}
-
-static SymresStatus symres_process_directive(AstNode* directive) {
- // :EliminatingSymres
- 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, Error_Critical, "#match directive expects a matched procedure.");
- return Symres_Error;
- }
-
- AstOverloadedFunction* ofunc = (AstOverloadedFunction *) add_overload->overloaded_function;
- if (ofunc->locked) {
- onyx_report_error(add_overload->token->pos, Error_Critical, "Cannot add match option here as the original #match was declared as #locked.");
- onyx_report_error(ofunc->token->pos, Error_Critical, "Here is the original #match.");
- return Symres_Error;
- }
-
- if (ofunc->only_local_functions) {
- if (!token_same_file(add_overload->token, ofunc->token)) {
- onyx_report_error(add_overload->token->pos, Error_Critical, "Cannot add match option here as this option is not within the same file as the original #match declared with #local.");
- onyx_report_error(ofunc->token->pos, Error_Critical, "Here is the original #match.");
- }
- }
-
- 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, Error_Critical, "This cannot be used as an operator overload.");
- return Symres_Error;
- }
-
- if (operator->operator != Binary_Op_Subscript_Equals && bh_arr_length(overload->params) != 2) {
- onyx_report_error(operator->token->pos, Error_Critical, "Expected exactly 2 arguments for binary operator overload.");
- 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);
- SYMRES(expression, &export->export_name_expr);
-
- if (export->export->kind == Ast_Kind_Polymorphic_Proc) {
- onyx_report_error(export->token->pos, Error_Critical, "Cannot export a polymorphic function.");
- return Symres_Error;
- }
-
- if (export->export->kind == Ast_Kind_Function) {
- AstFunction *func = (AstFunction *) export->export;
- func->exported_name = export->export_name;
- func->is_exported = 1;
-
- if (func->is_exported) {
- if (func->is_foreign) {
- onyx_report_error(export->token->pos, Error_Critical, "Cannot export a foreign function.");
- return Symres_Error;
- }
-
- if (func->is_intrinsic) {
- onyx_report_error(export->token->pos, Error_Critical, "Cannot export an intrinsic function.");
- return Symres_Error;
- }
- }
- }
-
- break;
- }
-
- case Ast_Kind_Directive_Init: {
- AstDirectiveInit *init = (AstDirectiveInit *) directive;
- SYMRES(expression, &init->init_proc);
-
- if (init->dependencies) {
- bh_arr_each(AstDirectiveInit *, dependency, init->dependencies) {
- SYMRES(expression, (AstTyped **) dependency);
- }
- }
-
- break;
- }
-
- case Ast_Kind_Directive_Library: {
- AstDirectiveLibrary *library = (AstDirectiveLibrary *) directive;
- SYMRES(expression, &library->library_symbol);
- break;
- }
-
- case Ast_Kind_Injection: {
- AstInjection *inject = (AstInjection *) directive;
-
- if (inject->dest == NULL) {
- if (inject->full_loc == NULL) return Symres_Error;
-
- if (inject->full_loc->kind != Ast_Kind_Field_Access) {
- onyx_report_error(inject->token->pos, Error_Critical, "#inject expects a dot (a.b) expression for the injection point.");
- return Symres_Error;
- }
-
- AstFieldAccess *acc = (AstFieldAccess *) inject->full_loc;
- inject->dest = acc->expr;
- inject->symbol = acc->token;
- }
-
- SYMRES(expression, &inject->dest);
- SYMRES(expression, &inject->to_inject);
-
- Scope *scope = get_scope_from_node_or_create((AstNode *) inject->dest);
- if (scope == NULL) {
- onyx_report_error(inject->token->pos, Error_Critical, "Cannot #inject here.");
- return Symres_Error;
- }
-
- AstBinding *binding = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinding), Ast_Kind_Binding);
- binding->token = inject->symbol;
- binding->node = (AstNode *) inject->to_inject;
-
- Package *pac = NULL;
- if (inject->dest->kind == Ast_Kind_Package) {
- pac = ((AstPackage *) inject->dest)->package;
- }
-
- add_entities_for_node(NULL, (AstNode *) binding, scope, pac);
- return Symres_Complete;
- }
- }
-
- return Symres_Success;
-}
-
-static SymresStatus symres_macro(AstMacro* macro) {
- macro->flags |= Ast_Flag_Comptime;
-
- if (macro->body->kind == Ast_Kind_Function) {
- SYMRES(function_header, (AstFunction *) macro->body);
- }
- else if (macro->body->kind == Ast_Kind_Polymorphic_Proc) {
- SYMRES(polyproc, (AstFunction *) macro->body);
- }
-
- return Symres_Success;
-}
-
-static SymresStatus symres_constraint(AstConstraint* constraint) {
- switch (constraint->phase) {
- case Constraint_Phase_Cloning_Expressions:
- case Constraint_Phase_Waiting_To_Be_Queued: {
- SYMRES(expression, (AstTyped **) &constraint->interface);
-
- bh_arr_each(AstType *, type_arg, constraint->type_args) {
- SYMRES(type, type_arg);
- }
-
- return Symres_Success;
- }
-
- case Constraint_Phase_Checking_Expressions: {
- fori (i, constraint->expr_idx, bh_arr_length(constraint->exprs)) {
- SYMRES(expression, &constraint->exprs[i].expr);
-
- if (constraint->exprs[i].expected_type_expr) {
- SYMRES(type, &constraint->exprs[i].expected_type_expr);
- }
- }
-
- return Symres_Success;
- }
- }
-
- return Symres_Success;
-}
-
-static SymresStatus symres_polyquery(AstPolyQuery *query) {
- // :EliminatingSymres
- query->successful_symres = 0;
-
- if (query->function_header->scope == NULL)
- query->function_header->scope = scope_create(context.ast_alloc, query->proc->parent_scope_of_poly_proc, query->token->pos);
-
- scope_enter(query->function_header->scope);
-
- u32 idx = 0;
- bh_arr_each(AstParam, param, query->function_header->params) {
- bh_arr_each(AstPolyParam, pp, query->proc->poly_params) {
- if (pp->kind == PPK_Baked_Value && pp->idx == idx) goto skip_introducing_symbol;
- }
-
- symbol_introduce(curr_scope, param->local->token, (AstNode *) param->local);
-
- skip_introducing_symbol:
- idx++;
- }
-
- bh_arr_each(AstParam, param, query->function_header->params) {
- if (param->local->type_node != NULL) {
- resolved_a_symbol = 0;
-
- param->local->flags |= Ast_Flag_Symbol_Invisible;
- symres_type(¶m->local->type_node);
- param->local->flags &= ~Ast_Flag_Symbol_Invisible;
-
- onyx_clear_errors();
-
- if (resolved_a_symbol) query->successful_symres = 1;
- }
- }
-
- scope_leave();
- return Symres_Success;
-}
-
-static SymresStatus symres_foreign_block(AstForeignBlock *fb) {
- if (fb->scope == NULL)
- fb->scope = scope_create(context.ast_alloc, curr_scope, fb->token->pos);
-
- bh_arr_each(Entity *, pent, fb->captured_entities) {
- Entity *ent = *pent;
- if (ent->type == Entity_Type_Function_Header) {
- if (ent->function->body->next != NULL) {
- onyx_report_error(ent->function->token->pos, Error_Critical, "Procedures declared in a #foreign block should not have bodies.");
- return Symres_Error;
- }
-
- ent->function->foreign_name = ent->function->intrinsic_name; // Hmm... This might not be right?
- ent->function->foreign_module = fb->module_name;
- ent->function->is_foreign = 1;
- ent->function->entity = NULL;
- ent->function->entity_header = NULL;
- ent->function->entity_body = NULL;
-
- add_entities_for_node(NULL, (AstNode *) ent->function, ent->scope, ent->package);
- continue;
- }
-
- if (ent->type == Entity_Type_Binding) {
- AstBinding* new_binding = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinding), Ast_Kind_Binding);
- new_binding->token = ent->binding->token;
- new_binding->node = ent->binding->node;
-
- Entity e;
- memset(&e, 0, sizeof(e));
- e.type = Entity_Type_Binding;
- e.state = Entity_State_Introduce_Symbols;
- e.binding = new_binding;
- e.scope = fb->scope;
- e.package = ent->package;
-
- entity_heap_insert(&context.entities, e);
- }
-
- if (ent->type != Entity_Type_Function) {
- entity_heap_insert_existing(&context.entities, ent);
- }
- }
-
- return Symres_Complete;
-}
-
-static SymresStatus symres_include(AstInclude* include) {
- if (include->name != NULL) return Symres_Goto_Parse;
-
- SYMRES(expression, &include->name_node);
-
- if (include->name_node->kind != Ast_Kind_StrLit) {
- onyx_report_error(include->token->pos, Error_Critical, "Expected compile-time known string literal here. Got '%s'.", onyx_ast_node_kind_string(include->name_node->kind));
- return Symres_Error;
- }
-
- OnyxToken* str_token = include->name_node->token;
- if (str_token != NULL) {
- token_toggle_end(str_token);
- include->name = bh_strdup(context.ast_alloc, str_token->text);
- string_process_escape_seqs(include->name, include->name, strlen(include->name));
- token_toggle_end(str_token);
- }
-
- return Symres_Goto_Parse;
-}
-
-static SymresStatus symres_file_contents(AstFileContents* fc) {
- SYMRES(expression, &fc->filename_expr);
-
- if (fc->filename_expr->kind != Ast_Kind_StrLit) {
- onyx_report_error(fc->token->pos, Error_Critical, "Expected given expression to be a compile-time stirng literal.");
- return Symres_Error;
- }
-
- return Symres_Success;
-}
-
-void symres_entity(Entity* ent) {
- if (ent->scope) scope_enter(ent->scope);
-
- report_unresolved_symbols = 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_Load_Path:
- case Entity_Type_Load_File: ss = symres_include(ent->include); break;
- case Entity_Type_File_Contents: ss = symres_file_contents(ent->file_contents); break;
-
- case Entity_Type_Foreign_Function_Header:
- case Entity_Type_Temp_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_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_Polymorphic_Proc: ss = symres_polyproc(ent->poly_proc);
- 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_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;
- case Entity_Type_Constraint_Check: ss = symres_constraint(ent->constraint); break;
- case Entity_Type_Polymorph_Query: ss = symres_polyquery(ent->poly_query); break;
- case Entity_Type_Foreign_Block: ss = symres_foreign_block(ent->foreign_block);
- if (context.options->generate_foreign_info) {
- next_state = Entity_State_Check_Types;
- ss = Symres_Success;
- }
- break;
-
- default: break;
- }
-
- if (ss == Symres_Yield_Macro) ent->macro_attempts++;
- if (ss == Symres_Yield_Micro) ent->micro_attempts++;
- if (ss == Symres_Complete) ent->state = Entity_State_Finalized;
- if (ss == Symres_Goto_Parse) ent->state = Entity_State_Parse;
- if (ss == Symres_Success) {
- ent->macro_attempts = 0;
- ent->micro_attempts = 0;
- ent->state = next_state;
- }
-
- curr_scope = NULL;
-}
+++ /dev/null
-#define BH_DEBUG
-#include "stb_ds.h"
-#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, POINTER_SIZE, POINTER_SIZE, "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_array_map;
-static bh_imap type_slice_map;
-static bh_imap type_dynarr_map;
-static bh_imap type_vararg_map;
-static 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_array_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);
- sh_new_arena(type_func_map);
-
- 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;
-
- bh_arr(StructMember *) members = t1_struct->Struct.memarr;
- if (bh_arr_length(members) > 0 && members[0]->used)
- return types_are_compatible(t2_struct,members[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.elem, t2->Slice.elem);
- }
-
- case Type_Kind_VarArgs: {
- if (t2->kind != Type_Kind_VarArgs) return 0;
- return types_are_compatible(t1->VarArgs.elem, t2->VarArgs.elem);
- }
-
- case Type_Kind_DynArray: {
- if (t2->kind != Type_Kind_DynArray) return 0;
- return types_are_compatible(t1->DynArray.elem, t2->DynArray.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;
- }
-
- case Type_Kind_Distinct:
- // If the above cases didn't catch it, then these distinct types are not compatible.
- return 0;
-
- 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 POINTER_SIZE;
- 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 POINTER_SIZE * 2; // HACK: These should not have to be 16 bytes in size, they should only have to be 12,
- case Type_Kind_VarArgs: return POINTER_SIZE * 2; // 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 POINTER_SIZE + 8 + 2 * POINTER_SIZE; // data (8), count (4), capacity (4), allocator { func (4), ---(4), data (8) }
- case Type_Kind_Compound: return type->Compound.size;
- case Type_Kind_Distinct: return type_size_of(type->Distinct.base_type);
- 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 POINTER_SIZE;
- 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 POINTER_SIZE;
- case Type_Kind_VarArgs: return POINTER_SIZE;
- case Type_Kind_DynArray: return POINTER_SIZE;
- case Type_Kind_Compound: return 4; // HACK
- case Type_Kind_Distinct: return type_alignment_of(type->Distinct.base_type);
- default: return 1;
- }
-}
-
-// If this function returns NULL, then the caller MUST yield because the type may still be constructed in the future.
-// If there was an error constructing the type, then this function will report that directly.
-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: {
- // ((AstPointerType *) type_node)->elem->flags |= type_node->flags & Ast_Flag_Header_Check_No_Error;
- 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 (func_type->Function.return_type != &type_auto_return) {
- i32 index = shgeti(type_func_map, name);
- if (index != -1) {
- u64 id = type_func_map[index].value;
- 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);
- shput(type_func_map, name, func_type->id);
-
- return func_type;
- }
-
- case Ast_Kind_Array_Type: {
- AstArrayType* a_node = (AstArrayType *) type_node;
-
- Type *elem_type = type_build_from_ast(alloc, a_node->elem);
- if (elem_type == NULL) return NULL;
-
- 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(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, Error_Critical, "Array type expects type 'i32' for size, got '%s'.",
- type_get_name(a_node->count_expr->type));
- return NULL;
- }
-
- count = get_expression_integer_value(a_node->count_expr, NULL);
- }
-
- Type* array_type = type_make_array(alloc, elem_type, count);
- if (array_type) array_type->ast_type = type_node;
- return array_type;
- }
-
- case Ast_Kind_Struct_Type: {
- AstStructType* s_node = (AstStructType *) type_node;
- if (s_node->stcache != NULL) return s_node->stcache;
- if (s_node->pending_type != NULL && s_node->pending_type_is_valid) return s_node->pending_type;
- if (!s_node->ready_to_build_type) return NULL;
-
- Type* s_type;
- if (s_node->pending_type == NULL) {
- s_type = type_create(Type_Kind_Struct, alloc, 0);
- s_node->pending_type = 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);
- s_type->Struct.meta_tags = s_node->meta_tags;
- s_type->Struct.constructed_from = NULL;
- s_type->Struct.status = SPS_Start;
- type_register(s_type);
-
- s_type->Struct.memarr = NULL;
- sh_new_arena(s_type->Struct.members);
- bh_arr_new(global_heap_allocator, s_type->Struct.memarr, s_type->Struct.mem_count);
-
- } else {
- s_type = s_node->pending_type;
- }
-
- s_type->Struct.poly_sln = NULL;
-
- bh_arr_clear(s_type->Struct.memarr);
- shfree(s_type->Struct.members);
- sh_new_arena(s_type->Struct.members);
-
- s_node->pending_type_is_valid = 1;
-
- b32 is_union = s_node->is_union;
- 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->pending_type_is_valid = 0;
- return NULL;
- }
-
- mem_alignment = type_alignment_of((*member)->type);
- if (mem_alignment <= 0) {
- onyx_report_error((*member)->token->pos, Error_Critical, "Invalid member type: %s. Has alignment %d", type_get_name((*member)->type), mem_alignment);
- return NULL;
- }
-
- if (mem_alignment > alignment) alignment = mem_alignment;
-
- if (!s_node->is_packed) {
- bh_align(offset, mem_alignment);
- }
-
- token_toggle_end((*member)->token);
- if (shgeti(s_type->Struct.members, (*member)->token->text) != -1) {
- onyx_report_error((*member)->token->pos, Error_Critical, "Duplicate struct member, '%s'.", (*member)->token->text);
- return NULL;
- }
-
- StructMember* smem = bh_alloc_item(alloc, StructMember);
- smem->offset = offset;
- smem->type = (*member)->type;
- smem->idx = idx;
- smem->name = bh_strdup(alloc, (*member)->token->text);
- smem->token = (*member)->token;
- smem->initial_value = &(*member)->initial_value;
- smem->meta_tags = (*member)->meta_tags;
-
- smem->included_through_use = 0;
- smem->used = (*member)->is_used;
- smem->use_through_pointer_index = -1;
- shput(s_type->Struct.members, (*member)->token->text, smem);
- bh_arr_push(s_type->Struct.memarr, smem);
- token_toggle_end((*member)->token);
-
- u32 type_size = type_size_of((*member)->type);
-
- if (!is_union) {
- offset += type_size;
- size = offset;
- } else {
- size = bh_max(size, type_size);
- }
-
- idx++;
- }
-
- u32 min_alignment = get_expression_integer_value(s_node->min_alignment_, NULL);
- alignment = bh_max(min_alignment, alignment);
- if (!s_node->is_packed) {
- bh_align(size, alignment);
- }
-
- u32 min_size = get_expression_integer_value(s_node->min_size_, NULL);
- size = bh_max(min_size, size);
-
- s_type->Struct.alignment = alignment;
- 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);
-
- s_type->Struct.status = SPS_Members_Done;
- 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->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: {
- if (type_node->type_id != 0) return NULL;
-
- Type* p_type = type_create(Type_Kind_PolyStruct, alloc, 0);
- p_type->ast_type = type_node;
- p_type->PolyStruct.name = ((AstPolyStructType *) type_node)->name;
- p_type->PolyStruct.meta_tags = ((AstPolyStructType *) type_node)->base_struct->meta_tags;
-
- type_register(p_type);
- return NULL;
- }
-
- 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, Error_Critical, "Cannot instantiate a concrete type off of a non-polymorphic type.");
- onyx_report_error(pc_type->callee->token->pos, Error_Critical, "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,
- }));
- }
- }
-
- Type* concrete = polymorphic_struct_lookup(ps_type, slns, pc_type->token->pos, (pc_type->flags & Ast_Flag_Header_Check_No_Error) == 0);
-
- // 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;
- if (concrete == (Type *) &node_that_signals_failure) return concrete;
- concrete->Struct.constructed_from = (AstType *) ps_type;
- return concrete;
- }
-
- 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;
- }
-
- case Ast_Kind_Distinct_Type: {
- AstDistinctType* distinct = (AstDistinctType *) type_node;
- if (distinct->dtcache) return distinct->dtcache;
-
- Type *base_type = type_build_from_ast(alloc, distinct->base_type);
- if (base_type == NULL) return NULL;
- if (base_type->kind != Type_Kind_Basic) {
- onyx_report_error(distinct->token->pos, Error_Critical, "Distinct types can only be made out of primitive types. '%s' is not a primitive type.", type_get_name(base_type));
- return NULL;
- }
-
- Type *distinct_type = type_create(Type_Kind_Distinct, alloc, 0);
- distinct_type->Distinct.base_type = base_type;
- distinct_type->Distinct.name = distinct->name;
- distinct->dtcache = distinct_type;
-
- type_register(distinct_type);
- return distinct_type;
- }
- }
-
- 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 (func_type->Function.return_type != &type_auto_return) {
- i32 index = shgeti(type_func_map, name);
- if (index != -1) {
- u64 id = type_func_map[index].value;
- 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);
- shput(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;
- if (compound->exprs[i]->type->kind == Type_Kind_Basic) {
- if (compound->exprs[i]->type->Basic.kind == Basic_Kind_Int_Unsized || compound->exprs[i]->type->Basic.kind == Basic_Kind_Float_Unsized) {
- 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_build_implicit_type_of_struct_literal(bh_allocator alloc, AstStructLiteral* lit) {
- Type* type = type_create(Type_Kind_Struct, alloc, 0);
- type->ast_type = NULL;
- type->Struct.name = NULL;
- type->Struct.mem_count = bh_arr_length(lit->args.named_values);
- type->Struct.meta_tags = NULL;
- type->Struct.constructed_from = NULL;
- type->Struct.status = SPS_Start;
- type->Struct.poly_sln = NULL;
- type_register(type);
-
- type->Struct.memarr = NULL;
- sh_new_arena(type->Struct.members);
- bh_arr_new(global_heap_allocator, type->Struct.memarr, type->Struct.mem_count);
-
- u32 size = 0;
- u32 offset = 0;
- u32 alignment = 1;
- u32 idx = 0;
- bh_arr_each(AstNamedValue *, pnv, lit->args.named_values) {
- AstNamedValue *nv = *pnv;
-
- Type* member_type = resolve_expression_type(nv->value);
- if (member_type == NULL) {
- return NULL;
- }
-
- u32 mem_alignment = type_alignment_of(member_type);
- if (mem_alignment <= 0) {
- return NULL;
- }
-
- alignment = bh_max(alignment, mem_alignment);
-
- // Should these structs be packed or not?
- bh_align(offset, mem_alignment);
-
- token_toggle_end(nv->token);
- if (shgeti(type->Struct.members, nv->token->text) != -1) {
- token_toggle_end(nv->token);
- return NULL;
- }
-
- StructMember *smem = bh_alloc_item(alloc, StructMember);
- smem->offset = offset;
- smem->type = member_type;
- smem->idx = idx;
- smem->name = bh_strdup(alloc, nv->token->text);
- smem->token = nv->token;
- smem->initial_value = &nv->value;
- smem->meta_tags = NULL;
- smem->included_through_use = 0;
- smem->used = 0;
- smem->use_through_pointer_index = -1;
- shput(type->Struct.members, nv->token->text, smem);
- bh_arr_push(type->Struct.memarr, smem);
- token_toggle_end(nv->token);
-
- u32 type_size = type_size_of(member_type);
- offset += type_size;
- size = offset;
- idx++;
- }
-
- type->Struct.alignment = alignment;
- type->Struct.size = size;
- type->Struct.linear_members = NULL;
- bh_arr_new(global_heap_allocator, type->Struct.linear_members, type->Struct.mem_count);
- build_linear_types_with_offset(type, &type->Struct.linear_members, 0);
-
- type->Struct.status = SPS_Uses_Done;
- return type;
-}
-
-Type* type_make_pointer(bh_allocator alloc, Type* to) {
- if (to == NULL) return NULL;
- if (to == (Type *) &node_that_signals_failure) return to;
-
- 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 = POINTER_SIZE;
- 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;
- if (to == (Type *) &node_that_signals_failure) return to;
-
- assert(to->id > 0);
- u64 key = ((((u64) to->id) << 32) | (u64) count);
- u64 array_id = bh_imap_get(&type_array_map, key);
- if (array_id > 0) {
- Type* array_type = (Type *) bh_imap_get(&type_map, array_id);
- return array_type;
-
- } else {
- 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);
-
- type_register(arr_type);
- bh_imap_put(&type_array_map, key, arr_type->id);
-
- return arr_type;
- }
-}
-
-Type* type_make_slice(bh_allocator alloc, Type* of) {
- if (of == NULL) return NULL;
- if (of == (Type *) &node_that_signals_failure) return of;
-
- 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);
-
- type_make_pointer(alloc, of);
- slice_type->Slice.elem = of;
-
- return slice_type;
- }
-}
-
-Type* type_make_dynarray(bh_allocator alloc, Type* of) {
- if (of == NULL) return NULL;
- if (of == (Type *) &node_that_signals_failure) return of;
-
- 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);
-
- type_make_pointer(alloc, of);
- dynarr->DynArray.elem = of;
-
- return dynarr;
- }
-}
-
-Type* type_make_varargs(bh_allocator alloc, Type* of) {
- if (of == NULL) return NULL;
- if (of == (Type *) &node_that_signals_failure) return of;
-
- 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);
-
- type_make_pointer(alloc, of);
- va_type->VarArgs.elem = 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;
- }
-}
-
-b32 type_struct_member_apply_use(bh_allocator alloc, Type *s_type, StructMember *smem) {
- Type* used_type = smem->type;
-
- b32 type_is_pointer = 0;
- if (used_type->kind == Type_Kind_Pointer) {
- type_is_pointer = 1;
- used_type = type_get_contained_type(used_type);
- }
-
- if (used_type->kind != Type_Kind_Struct) {
- onyx_report_error(smem->token->pos, Error_Critical, "Can only use things of structure, or pointer to structure type.");
- return 0;
- }
-
- if (used_type->Struct.status == SPS_Start) return 0;
-
- bh_arr_each(StructMember*, psmem, used_type->Struct.memarr) {
- if (shgeti(s_type->Struct.members, (*psmem)->name) != -1) {
- onyx_report_error(smem->token->pos, Error_Critical, "Used name '%s' conflicts with existing struct member.", (*psmem)->name);
- return 0;
- }
-
- StructMember* new_smem = bh_alloc_item(alloc, StructMember);
- new_smem->type = (*psmem)->type;
- new_smem->name = (*psmem)->name;
- new_smem->meta_tags = (*psmem)->meta_tags;
- new_smem->used = 0;
- new_smem->included_through_use = 1;
-
- if (type_is_pointer) {
- new_smem->offset = (*psmem)->offset;
- new_smem->idx = (*psmem)->idx;
- new_smem->initial_value = NULL;
- new_smem->use_through_pointer_index = smem->idx;
- } else {
- new_smem->offset = smem->offset + (*psmem)->offset;
- new_smem->idx = -1; // Dummy value because I don't think this is needed.
- new_smem->initial_value = (*psmem)->initial_value;
- new_smem->use_through_pointer_index = -1;
- }
-
- shput(s_type->Struct.members, (*psmem)->name, new_smem);
- }
-
- return 1;
-}
-
-const char* type_get_unique_name(Type* type) {
- if (type == NULL) return "unknown";
-
- switch (type->kind) {
- case Type_Kind_Basic: return type->Basic.name;
- case Type_Kind_Pointer: return bh_aprintf(global_scratch_allocator, "^%s", type_get_unique_name(type->Pointer.elem));
- case Type_Kind_Array: return bh_aprintf(global_scratch_allocator, "[%d] %s", type->Array.count, type_get_unique_name(type->Array.elem));
- case Type_Kind_Struct:
- if (type->Struct.name)
- return bh_aprintf(global_scratch_allocator, "%s@%l", type->Struct.name, type->id);
- else
- return bh_aprintf(global_scratch_allocator, "%s@%l", "<anonymous struct>", type->id);
- case Type_Kind_Enum:
- if (type->Enum.name)
- return bh_aprintf(global_scratch_allocator, "%s@%l", type->Enum.name, type->id);
- else
- return bh_aprintf(global_scratch_allocator, "%s@%l", "<anonymous enum>", type->id);
-
- case Type_Kind_Slice: return bh_aprintf(global_scratch_allocator, "[] %s", type_get_unique_name(type->Slice.elem));
- case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_unique_name(type->VarArgs.elem));
- case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_unique_name(type->DynArray.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);
- }
-
- case Type_Kind_Distinct: {
- return bh_aprintf(global_scratch_allocator, "%s@%l", type->Distinct.name, type->id);
- }
-
- default: return "unknown (not null)";
- }
-}
-
-const char* type_get_name(Type* type) {
- if (type == NULL) return "unknown";
-
- switch (type->kind) {
- case Type_Kind_Basic: return type->Basic.name;
- case Type_Kind_Pointer: return bh_aprintf(global_scratch_allocator, "^%s", type_get_name(type->Pointer.elem));
- case Type_Kind_Array: return bh_aprintf(global_scratch_allocator, "[%d] %s", type->Array.count, type_get_name(type->Array.elem));
- case Type_Kind_Struct:
- if (type->Struct.name)
- return type->Struct.name;
- else
- return "<anonymous struct>";
- case Type_Kind_Enum:
- if (type->Enum.name)
- return type->Enum.name;
- else
- return "<anonymous enum>";
-
- case Type_Kind_Slice: return bh_aprintf(global_scratch_allocator, "[] %s", type_get_name(type->Slice.elem));
- case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_name(type->VarArgs.elem));
- case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_name(type->DynArray.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);
- }
-
- case Type_Kind_Distinct: {
- return bh_aprintf(global_scratch_allocator, "%s", type->Distinct.name);
- }
-
- 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;
-}
-
-Type* type_get_contained_type(Type* type) {
- if (type == NULL) return NULL;
- switch (type->kind) {
- case Type_Kind_Pointer: return type->Pointer.elem;
- case Type_Kind_Array: return type->Array.elem;
- case Type_Kind_Slice: return type->Slice.elem;
- case Type_Kind_DynArray: return type->DynArray.elem;
- case Type_Kind_VarArgs: return type->VarArgs.elem;
- default: return NULL;
- }
-}
-
-static const StructMember slice_members[] = {
- { 0, 0, NULL, "data", NULL, NULL, -1, 0, 0 },
- { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "count", NULL, NULL, -1, 0, 0 },
- { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "size", NULL, NULL, -1, 0, 0 },
- { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "length", NULL, NULL, -1, 0, 0 },
-};
-
-static const StructMember array_members[] = {
- { 0, 0, NULL, "data", NULL, NULL, -1, 0, 0 },
- { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "count", NULL, NULL, -1, 0, 0 },
- { POINTER_SIZE + 4, 2, &basic_types[Basic_Kind_U32], "capacity", NULL, NULL, -1, 0, 0 },
- { POINTER_SIZE + 8, 3, NULL, "allocator", NULL, NULL, -1, 0, 0 },
- { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "size", NULL, NULL, -1, 0, 0 },
- { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "length", NULL, NULL, -1, 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;
-
- i32 index = shgeti(stype->members, member);
- if (index == -1) return 0;
- *smem = *stype->members[index].value;
- 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_make_pointer(context.ast_alloc, type->Slice.elem);
-
- 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_make_pointer(context.ast_alloc, type->DynArray.elem);
- 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_make_pointer(context.ast_alloc, type->Slice.elem);
-
- return 1;
- }
-
- case Type_Kind_DynArray: {
- if (idx > 4) return 0;
-
- *smem = array_members[idx];
- if (idx == 0) smem->type = type_make_pointer(context.ast_alloc, type->DynArray.elem);
- 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_make_pointer(context.ast_alloc, type->Slice.elem);
- two->offset = 0;
- }
- if (idx == 1) {
- two->type = &basic_types[Basic_Kind_U32];
- two->offset = POINTER_SIZE;
- }
-
- return 1;
- }
- case Type_Kind_DynArray: {
- if (idx == 0) {
- two->type = type_make_pointer(context.ast_alloc, type->DynArray.elem);
- two->offset = 0;
- }
- if (idx == 1) {
- two->type = &basic_types[Basic_Kind_U32];
- two->offset = POINTER_SIZE;
- }
- if (idx == 2) {
- two->type = &basic_types[Basic_Kind_U32];
- two->offset = POINTER_SIZE + 4;
- }
- 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 += POINTER_SIZE + 8;
- }
-
- 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;
-
- case Type_Kind_Distinct:
- two->type = type->Distinct.base_type;
- two->offset = 0;
- 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 == POINTER_SIZE) return 1;
- return -1;
- }
- case Type_Kind_DynArray: {
- if (offset == 0) return 0;
- if (offset == POINTER_SIZE) return 1;
- if (offset == POINTER_SIZE + 4) return 2;
- if (offset == POINTER_SIZE + 8) return 3;
- if (offset == POINTER_SIZE * 2 + 8) 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) || (*mem)->type->kind == Type_Kind_Array) {
- 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_Enum) return type_is_small_integer(type->Enum.backing);
- if (type->kind == Type_Kind_Distinct) return type_is_small_integer(type->Distinct.base_type);
- 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_Enum) return type_is_integer(type->Enum.backing);
- if (type->kind == Type_Kind_Distinct) return type_is_integer(type->Distinct.base_type);
- 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_Distinct) return type_is_numeric(type->Distinct.base_type);
- 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;
-
- if (type->kind == Type_Kind_Struct) {
- //
- // This is for the kind of common case where a structure simply wraps a
- // single non-compound value; in this situation, the structure can be
- // "dissolved" at compile-time and turn into the underlying type.
- //
-
- if (bh_arr_length(type->Struct.linear_members) != 1) return 1;
- return type_is_compound(type->Struct.linear_members[0].type);
- }
-
- 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
- && type->kind != Type_Kind_Distinct;
-}
-
-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_Array) return 1;
- if (type->kind == Type_Kind_Struct) return 1;
- if (type->kind == Type_Kind_Slice) return 1;
- if (type->kind == Type_Kind_DynArray) return 1;
- if (type->kind == Type_Kind_VarArgs) return 1;
- if (type->kind == Type_Kind_Pointer) {
- if (type->Pointer.elem->kind == Type_Kind_Struct) return 1;
- if (type->Pointer.elem->kind == Type_Kind_Slice) return 1;
- if (type->Pointer.elem->kind == Type_Kind_DynArray) return 1;
- }
- return 0;
-}
-
-b32 type_is_structlike_strict(Type* type) {
- if (type == NULL) return 0;
- if (type->kind == Type_Kind_Struct) return 1;
- if (type->kind == Type_Kind_Slice) return 1;
- if (type->kind == Type_Kind_DynArray) return 1;
- if (type->kind == Type_Kind_VarArgs) return 1;
- return 0;
-}
-
-u32 type_structlike_mem_count(Type* type) {
- if (type == NULL) return 0;
- switch (type->kind) {
- case Type_Kind_Struct: return type->Struct.mem_count;
- case Type_Kind_Slice: return 2;
- case Type_Kind_VarArgs: return 2;
- case Type_Kind_DynArray: return 4;
- default: return 0;
- }
-}
-
-u32 type_structlike_is_simple(Type* type) {
- if (type == NULL) return 0;
- switch (type->kind) {
- case Type_Kind_Struct: return type_struct_is_simple(type);
- case Type_Kind_Slice: return 1;
- case Type_Kind_VarArgs: return 1;
- case Type_Kind_DynArray: return 0;
- default: return 0;
- }
-}
-
-b32 type_is_sl_constructable(Type* type) {
- if (type == NULL) return 0;
- switch (type->kind) {
- case Type_Kind_Struct: return 1;
- case Type_Kind_Slice: return 1;
- case Type_Kind_DynArray: return 1;
- default: return 0;
- }
-}
-
-b32 type_struct_constructed_from_poly_struct(Type* struct_type, struct AstType* from) {
- if (struct_type == NULL) return 0;
- if (struct_type->kind != Type_Kind_Struct) return 0;
-
- return struct_type->Struct.constructed_from == from;
-}
+++ /dev/null
-#define BH_DEBUG
-
-#include "utils.h"
-#include "lex.h"
-#include "astnodes.h"
-#include "errors.h"
-#include "parser.h"
-#include "astnodes.h"
-#include "errors.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) {
- i32 index = shgeti(context.packages, package_name);
- if (index != -1) {
- return context.packages[index].value;
- } else {
- return NULL;
- }
-}
-
-Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc, OnyxFilePos pos) {
- i32 index = shgeti(context.packages, package_name);
- if (index != -1) {
- return context.packages[index].value;
-
- } 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->use_package_entities = NULL;
-
- if (!strcmp(pac_name, "builtin")) {
- package->private_scope = scope_create(alloc, context.global_scope, pos);
- package->scope = context.global_scope;
- } else {
- package->scope = scope_create(alloc, parent_scope, pos);
- package->private_scope = scope_create(alloc, package->scope, pos);
- }
-
- shput(context.packages, pac_name, package);
-
- if (!charset_contains(pac_name, '.')) {
- AstPackage* package_node = onyx_ast_node_new(alloc, sizeof(AstPackage), Ast_Kind_Package);
- package_node->package_name = package->name;
- package_node->package = package;
-
- symbol_raw_introduce(context.global_scope, pac_name, pos, (AstNode *) package_node);
- }
-
- return package;
- }
-}
-
-void package_track_use_package(Package* package, Entity* entity) {
- assert(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) return;
- 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->name = NULL;
-
- scope->symbols = NULL;
- sh_new_arena(scope->symbols);
-
- return scope;
-}
-
-void scope_include(Scope* target, Scope* source, OnyxFilePos pos) {
- fori (i, 0, shlen(source->symbols)) {
- symbol_raw_introduce(target, source->symbols[i].key, pos, source->symbols[i].value);
- }
-}
-
-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, "_")) {
- i32 index = shgeti(scope->symbols, name);
- if (index != -1) {
- AstNode *node = scope->symbols[index].value;
- if (node != symbol) {
- onyx_report_error(pos, Error_Critical, "Redeclaration of symbol '%s'.", name);
-
- if (node->token) {
- onyx_report_error(node->token->pos, Error_Critical, "Previous declaration was here.");
- }
-
- return 0;
- }
- return 1;
- }
- }
-
- shput(scope->symbols, name, symbol);
- return 1;
-}
-
-void symbol_builtin_introduce(Scope* scope, char* sym, AstNode *node) {
- shput(scope->symbols, sym, node);
-}
-
-void symbol_subpackage_introduce(Scope* scope, char* sym, AstPackage* package) {
- i32 index = shgeti(scope->symbols, sym);
- if (index != -1) {
- AstNode* maybe_package = scope->symbols[index].value;
-
- // CLEANUP: Make this assertion an actual error message.
- assert(maybe_package->kind == Ast_Kind_Package);
- } else {
- shput(scope->symbols, sym, (AstNode *) package);
- }
-}
-
-AstNode* symbol_raw_resolve(Scope* start_scope, char* sym) {
- Scope* scope = start_scope;
-
- while (scope != NULL) {
- i32 index = shgeti(scope->symbols, sym);
- if (index != -1) {
- AstNode* res = scope->symbols[index].value;
-
- if ((res->flags & Ast_Flag_Symbol_Invisible) == 0) {
- return res;
- }
- }
-
- scope = scope->parent;
- }
-
- return NULL;
-}
-
-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_Foreign_Block: {
- AstForeignBlock* fb = (AstForeignBlock *) node;
-
- if (fb->scope == NULL)
- return NULL;
-
- return symbol_raw_resolve(fb->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;
-
- // HACK HACK
- // Temporarily disable the parent scope so that you can't access things
- // "above" the structures scope. This leads to unintended behavior, as when
- // you are accessing a static element on a structure, you don't expect to
- // bleed to the top level scope.
- AstNode *result = NULL;
- if (stype->scope) {
- Scope *tmp_parent = stype->scope->parent;
- stype->scope->parent = NULL;
- result = symbol_raw_resolve(stype->scope, symbol);
- stype->scope->parent = tmp_parent;
- }
-
- 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);
- }
-
- case Ast_Kind_Poly_Call_Type: {
- AstNode* callee = (AstNode *) ((AstPolyCallType *) node)->callee;
- return try_symbol_raw_resolve_from_node(callee, 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;
-}
-
-AstNode* try_symbol_raw_resolve_from_type(Type *type, char* symbol) {
- while (type->kind == Type_Kind_Pointer) {
- type = type->Pointer.elem;
- }
-
- if (type->kind == Type_Kind_Struct) {
- if (type->Struct.poly_sln == NULL) return NULL;
-
- bh_arr_each(AstPolySolution, sln, type->Struct.poly_sln) {
- if (token_text_equals(sln->poly_sym->token, symbol)) {
- if (sln->kind == PSK_Type) {
- AstTypeRawAlias* alias = onyx_ast_node_new(context.ast_alloc, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
- alias->type = &basic_types[Basic_Kind_Type_Index];
- alias->to = sln->type;
- return (AstNode *) alias;
-
- } else {
- return (AstNode *) sln->value;
- }
- }
- }
- }
-
- return NULL;
-}
-
-void scope_clear(Scope* scope) {
- sh_new_arena(scope->symbols);
-}
-
-// Polymorphic procedures are in their own file to clean up this file.
-#include "polymorph.h"
-
-//
-// 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) {
- 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 *) strip_aliases((AstNode *) entry->key);
- arguments_copy(&args, param_args);
-
- AstFunction* overload = NULL;
- switch (node->kind) {
- case Ast_Kind_Macro: overload = macro_resolve_header((AstMacro *) node, param_args, NULL, 0); break;
- case Ast_Kind_Polymorphic_Proc: overload = polymorphic_proc_build_only_header((AstFunction *) node, PPLM_By_Arguments, param_args); break;
- case Ast_Kind_Function:
- overload = (AstFunction *) node;
- arguments_clear_baked_flags(&args);
- break;
- }
-
- // NOTE: Overload is not something that is known to be overloadable.
- if (overload == NULL) continue;
- if (overload == (AstFunction *) &node_that_signals_a_yield) return (AstTyped *) overload;
- 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.
-
- // 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 (AstTyped *) &node_that_signals_a_yield;
- }
- assert(overload->type->kind == Type_Kind_Function);
-
- arguments_remove_baked(&args);
- arguments_ensure_length(&args, get_argument_buffer_size(&overload->type->Function, &args));
-
- // NOTE: If the arguments cannot be placed successfully in the parameters list
- if (!fill_in_arguments(&args, (AstNode *) overload, NULL, 0)) continue;
-
- VarArgKind va_kind;
- TypeMatch tm = check_arguments_against_type(&args, &overload->type->Function, &va_kind, NULL, NULL, NULL);
- if (tm == TYPE_MATCH_SUCCESS) {
- matched_overload = node;
- break;
- }
-
- if (tm == TYPE_MATCH_YIELD) {
- return (AstTyped *) &node_that_signals_a_yield;
- }
- }
-
- 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;
-
- TypeMatch tm = unify_node_and_type(&node, type);
- if (tm == TYPE_MATCH_SUCCESS) {
- matched_overload = node;
- break;
- }
-
- if (tm == TYPE_MATCH_YIELD) {
- return (AstTyped *) &node_that_signals_a_yield;
- }
- }
-
- bh_imap_free(&all_overloads);
- return matched_overload;
-}
-
-void report_unable_to_match_overload(AstCall* call, bh_arr(OverloadOption) overloads) {
- 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, Error_Critical, "Unable to match overloaded function with provided argument types: (%s)", arg_str);
-
- bh_free(global_scratch_allocator, arg_str);
-
- // 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);
-
- i32 i = 1;
- bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
- AstTyped* node = (AstTyped *) strip_aliases((AstNode *) entry->key);
- onyx_report_error(node->token->pos, Error_Critical, "Here is one of the overloads. %d/%d", i++, bh_arr_length(all_overloads.entries));
- }
-
- bh_imap_free(&all_overloads);
-}
-
-
-//
-// 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.
- Type *any_type = type_build_from_ast(context.ast_alloc, builtin_any_type);
- fori (i, 0, bh_arr_length(call->args.values)) {
- AstNode *value = (AstNode *) ((AstArgument *) call->args.values[i])->value;
- assert(template->params[i].local->type);
-
- Type *param_type = template->params[i].local->type;
- if (param_type == any_type
- || (param_type->kind == Type_Kind_VarArgs && param_type->VarArgs.elem == any_type)) {
- onyx_report_error(macro->token->pos, Error_Critical, "Currently, macros do not support arguments of type 'any' or '..any'.");
- }
-
- symbol_introduce(argument_scope, template->params[i].local->token, value);
- }
-
- if (template->poly_scope != NULL)
- scope_include(argument_scope, template->poly_scope, call->token->pos);
-
- *(AstNode **) pcall = subst;
- return;
-}
-
-AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite, b32 error_if_failed) {
- switch (macro->body->kind) {
- case Ast_Kind_Function: return (AstFunction *) macro->body;
-
- case Ast_Kind_Polymorphic_Proc: {
- AstFunction* pp = (AstFunction *) macro->body;
- ensure_polyproc_cache_is_created(pp);
-
- bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, PPLM_By_Arguments, args, callsite, error_if_failed);
-
- if (slns == NULL) {
- if (flag_to_yield) {
- flag_to_yield = 0;
- return (AstFunction *) &node_that_signals_a_yield;
- }
-
- return NULL;
- }
-
- return polymorphic_proc_build_only_header_with_slns(pp, slns, error_if_failed);
- }
-
- default: assert(("Bad macro body type.", 0));
- }
-
- return NULL;
-}
-
-
-//
-// 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;
-}
-
-static i32 non_baked_argument_count(Arguments* args) {
- if (args->used_argument_count >= 0) return args->used_argument_count;
-
- i32 count = 0;
-
- bh_arr_each(AstTyped *, actual, args->values) {
- if ((*actual)->kind != Ast_Kind_Argument) count++;
- else if (!((AstArgument *) (*actual))->is_baked) count++;
- }
-
- bh_arr_each(AstNamedValue *, named_value, args->named_values) {
- if ((*named_value)->value->kind != Ast_Kind_Argument) count++;
- else if (!((AstArgument *) (*named_value)->value)->is_baked) count++;
- }
-
- args->used_argument_count = count;
- return count;
-}
-
-i32 get_argument_buffer_size(TypeFunction* type, Arguments* args) {
- i32 non_vararg_param_count = (i32) type->param_count;
- if (non_vararg_param_count > 0) {
- if (type->params[type->param_count - 1] == builtin_vararg_type_type) non_vararg_param_count--;
- if (type->params[type->param_count - 1]->kind == Type_Kind_VarArgs) non_vararg_param_count--;
- }
-
- return bh_max(non_vararg_param_count, non_baked_argument_count(args));
-}
-
-// 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, b32 insert_zero_values) {
-
- { // 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)) {
- if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "Error placing value with name '%s' at index '%d'.", named_value->token->text, idx);
- 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) {
- if (insert_zero_values) {
- assert(provider->token);
- args->values[idx] = (AstTyped *) make_zero_value(context.ast_alloc, provider->token, NULL);
- } else {
- if (err_msg) *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) {
- if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "Too many values provided. Expected at most %d.", maximum_arguments);
- success = 0;
- }
-
- return success;
-}
-
-
-//
-// Argument checking
-//
-
-typedef enum ArgState {
- AS_Expecting_Exact,
- AS_Expecting_Typed_VA,
- AS_Expecting_Untyped_VA,
-} ArgState;
-
-TypeMatch check_arguments_against_type(Arguments* args, TypeFunction* func_type, VarArgKind* va_kind,
- OnyxToken* location, char* func_name, OnyxError* error) {
- b32 permanent = location != NULL;
- if (func_name == NULL) func_name = "UNKNOWN FUNCTION";
-
- if (error) error->rank = Error_Critical;
-
- bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) args->values;
- i32 arg_count = get_argument_buffer_size(func_type, args);
-
- Type **formal_params = func_type->params;
- Type* variadic_type = NULL;
- i64 any_type_id = type_build_from_ast(context.ast_alloc, builtin_any_type)->id;
-
- ArgState arg_state = AS_Expecting_Exact;
- u32 arg_pos = 0;
- while (1) {
- switch (arg_state) {
- case AS_Expecting_Exact: {
- if (arg_pos >= func_type->param_count) goto type_checking_done;
-
- if (formal_params[arg_pos]->kind == Type_Kind_VarArgs) {
- variadic_type = formal_params[arg_pos]->VarArgs.elem;
- arg_state = AS_Expecting_Typed_VA;
- continue;
- }
-
- if ((i16) arg_pos == func_type->vararg_arg_pos) {
- arg_state = AS_Expecting_Untyped_VA;
- continue;
- }
-
- if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
-
- assert(arg_arr[arg_pos]->kind == Ast_Kind_Argument);
- TypeMatch tm = unify_node_and_type_(&arg_arr[arg_pos]->value, formal_params[arg_pos], permanent);
- if (tm == TYPE_MATCH_YIELD) return tm;
- if (tm == TYPE_MATCH_FAILED) {
- if (error != NULL) {
- error->pos = arg_arr[arg_pos]->token->pos;
- error->text = bh_aprintf(global_heap_allocator,
- "The procedure '%s' expects a value of type '%s' for %d%s parameter, got '%s'.",
- func_name,
- 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));
- }
- return tm;
- }
-
- if (arg_arr[arg_pos]->value->type && arg_arr[arg_pos]->value->type->id != any_type_id && formal_params[arg_pos]->id == any_type_id) {
- resolve_expression_type(arg_arr[arg_pos]->value);
- if (error != NULL) {
- arg_arr[arg_pos]->pass_as_any = 1;
- }
- }
-
- arg_arr[arg_pos]->va_kind = VA_Kind_Not_VA;
- break;
- }
-
- case AS_Expecting_Typed_VA: {
- if (variadic_type->id == any_type_id) *va_kind = VA_Kind_Any;
-
- if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
-
- if (variadic_type->id == any_type_id) {
- resolve_expression_type(arg_arr[arg_pos]->value);
- if (arg_arr[arg_pos]->value->type == NULL) {
- if (error != NULL) {
- error->pos = arg_arr[arg_pos]->token->pos;
- error->text = "Unable to resolve type of argument.";
- }
- return TYPE_MATCH_FAILED;
- }
-
- arg_arr[arg_pos]->va_kind = VA_Kind_Any;
- break;
- }
-
- *va_kind = VA_Kind_Typed;
-
- assert(arg_arr[arg_pos]->kind == Ast_Kind_Argument);
- TypeMatch tm = unify_node_and_type_(&arg_arr[arg_pos]->value, variadic_type, permanent);
- if (tm == TYPE_MATCH_YIELD) return tm;
- if (tm == TYPE_MATCH_FAILED) {
- if (error != NULL) {
- error->pos = arg_arr[arg_pos]->token->pos,
- error->text = bh_aprintf(global_heap_allocator,
- "The procedure '%s' expects a value of type '%s' for the variadic parameter, got '%s'.",
- func_name,
- type_get_name(variadic_type),
- node_get_type_name(arg_arr[arg_pos]->value));
- }
- return tm;
- }
-
- arg_arr[arg_pos]->va_kind = VA_Kind_Typed;
- break;
- }
-
- case AS_Expecting_Untyped_VA: {
- *va_kind = VA_Kind_Untyped;
-
- if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
-
- assert(arg_arr[arg_pos]->kind == Ast_Kind_Argument);
- resolve_expression_type(arg_arr[arg_pos]->value);
- if (arg_arr[arg_pos]->value->type == NULL) {
- if (error != NULL) {
- error->pos = arg_arr[arg_pos]->token->pos;
- error->text = "Unable to resolve type for argument.";
- }
- return TYPE_MATCH_FAILED;
- }
-
- arg_arr[arg_pos]->va_kind = VA_Kind_Untyped;
- break;
- }
- }
-
- arg_pos++;
- }
-
-type_checking_done:
- if (arg_pos < func_type->needed_param_count) {
- if (error != NULL) {
- error->pos = location->pos;
- error->text = "Too few arguments to function call.";
- }
- return TYPE_MATCH_FAILED;
- }
-
- if (arg_pos < (u32) arg_count) {
- if (error != NULL) {
- error->pos = location->pos;
- error->text = bh_aprintf(global_heap_allocator, "Too many arguments to function call. %d %d", arg_pos, arg_count);
- }
- return TYPE_MATCH_FAILED;
- }
-
- return TYPE_MATCH_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;
-}
-
-static Scope **get_scope_from_node_helper(AstNode *node) {
- 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;
- if (package->package == NULL) return NULL;
-
- return &package->package->scope;
- }
-
- case Ast_Kind_Enum_Type: {
- AstEnumType* etype = (AstEnumType *) node;
- return &etype->scope;
- }
-
- case Ast_Kind_Struct_Type: {
- AstStructType* stype = (AstStructType *) node;
- return &stype->scope;
- }
-
- case Ast_Kind_Poly_Struct_Type: {
- AstPolyStructType* pstype = (AstPolyStructType *) node;
- AstStructType* stype = pstype->base_struct;
- return &stype->scope;
- }
- }
-
- return NULL;
-}
-
-Scope *get_scope_from_node(AstNode *node) {
- if (!node) return NULL;
-
- Scope **pscope = get_scope_from_node_helper(node);
- if (!pscope) return NULL;
- return *pscope;
-}
-
-Scope *get_scope_from_node_or_create(AstNode *node) {
- if (!node) return NULL;
-
- Scope **pscope = get_scope_from_node_helper(node);
- if (!pscope) return NULL;
-
- // Create the scope if it does not exist.
- // This uses a NULL parent, which I think is what
- // is used in other parts of the compiler for struct/enum
- // scopes?
- if (!*pscope) {
- assert(node->token);
- *pscope = scope_create(context.ast_alloc, NULL, node->token->pos);
- }
-
- return *pscope;
-}
-
-u32 levenshtein_distance(const char *str1, const char *str2) {
- i32 m = strlen(str1) + 1;
- i32 n = strlen(str2) + 1;
-
- i32 *d = bh_alloc_array(global_scratch_allocator, i32, m * n);
- fori (i, 0, m * n) d[i] = 0;
-
- fori (i, 0, m) d[i * n + 0] = i;
- fori (j, 0, n) d[0 * n + j] = j;
-
- fori (j, 1, n) {
- fori (i, 1, m) {
- i32 subst_cost = str1[i - 1] == str2[j - 1] ? 0 : 1;
-
- i32 a = d[(i - 1) * n + j] + 1;
- i32 b = d[i * n + (j - 1)] + 1;
- i32 c = d[(i - 1) * n + (j - 1)] + subst_cost;
-
- d[i * n + j] = bh_min(bh_min(a, b), c);
- }
- }
-
- return d[m * n - 1];
-}
-
-char *find_closest_symbol_in_scope(Scope *scope, char *sym, u32 *out_distance) {
- *out_distance = 0x7fffffff;
-
- if (scope == NULL) return NULL;
-
- char* closest = NULL;
- fori (i, 0, shlen(scope->symbols)) {
- if (scope->symbols[i].value->flags & Ast_Flag_Symbol_Invisible) continue;
-
- char *key = scope->symbols[i].key;
- u32 d = levenshtein_distance(key, sym);
- if (d < *out_distance) {
- *out_distance = d;
- closest = (char *) key;
- }
- }
-
- return closest;
-}
-
-char *find_closest_symbol_in_scope_and_parents(Scope *scope, char *sym) {
- u32 min_dist = 0x7fffffff;
- u32 tmp_dist;
-
- char *closest = NULL;
- while (scope != NULL) {
- char *tmp_closest = find_closest_symbol_in_scope(scope, sym, &tmp_dist);
- if (tmp_dist < min_dist) {
- min_dist = tmp_dist;
- closest = tmp_closest;
- }
-
- scope = scope->parent;
- }
-
- return closest;
-}
-
-char *find_closest_symbol_in_node(AstNode* node, char *sym) {
- Scope *scope = get_scope_from_node(node);
- if (!scope) {
- if (node->kind == Ast_Kind_Poly_Call_Type) {
- AstPolyCallType* pcall = (AstPolyCallType *) node;
- return find_closest_symbol_in_node((AstNode *) pcall->callee, sym);
- }
-
- return NULL;
- }
-
- u32 dist;
- return find_closest_symbol_in_scope(scope, sym, &dist);
-}
-
+++ /dev/null
-//
-// There are several things I'm seeing in this file that I want to clean up.
-// They are:
-// [x] remove the need to know if the stack is needed before generating the function.
-// Just leave 5 nops at the beginning because they will be automatically removed
-// by the WASM outputter.
-// [x] remove the need to have "allocate_exprs" on blocks and in functions. This will
-// be easy once the above is done.
-// [x] there should be a better way to emit pending deferred statements because there
-// is some code duplication between emit_return and emit_structured_jump.
-// [ ] Change the calling convention so it is easier to use from both JS and in the compiler.
-
-
-
-
-#define BH_DEBUG
-#include "wasm_emit.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) {
- if (type_linear_member_count(type) == 1) {
- return onyx_type_to_wasm_type(type->Struct.linear_members[0].type);
- }
-
- return WASM_TYPE_VOID;
- }
-
- if (type->kind == Type_Kind_Slice) {
- return WASM_TYPE_VOID;
- }
-
- if (type->kind == Type_Kind_Compound) {
- return WASM_TYPE_VOID;
- }
-
- if (type->kind == Type_Kind_Enum) {
- return onyx_type_to_wasm_type(type->Enum.backing);
- }
-
- if (type->kind == Type_Kind_Distinct) {
- return onyx_type_to_wasm_type(type->Distinct.base_type);
- }
-
- 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_Distinct && local->type->Distinct.base_type->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;
-}
-
-
-static inline b32 should_emit_function(AstFunction* fd) {
- // NOTE: Don't output intrinsic functions
- if (fd->is_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->is_exported || bh_arr_length(fd->tags) > 0) {
- return 1;
- } else {
- return 0;
- }
- }
-
- return 1;
-}
-
-
-//
-// Debug Info Generation
-//
-
-#ifdef ENABLE_DEBUG_INFO
-
-static u32 debug_introduce_symbol(OnyxWasmModule *mod, OnyxToken *token, DebugSymLoc loc, u64 num, Type* type) {
-
- u32 id = mod->debug_context->next_sym_id++;
-
- DebugSymInfo sym_info;
- sym_info.sym_id = id;
- sym_info.location_type = loc;
- sym_info.location_num = num;
- sym_info.type = type->id;
-
- if (token) {
- token_toggle_end(token);
- sym_info.name = bh_strdup(context.ast_alloc, token->text);
- token_toggle_end(token);
- } else {
- sym_info.name = NULL;
- }
-
- bh_arr_push(mod->debug_context->sym_info, sym_info);
-
- if (loc == DSL_REGISTER) {
- assert(mod->local_alloc);
- DebugSymPatch patch;
- patch.func_idx = mod->current_func_idx;
- patch.sym_id = id;
- patch.local_idx = num;
- bh_arr_push(mod->debug_context->sym_patches, patch);
- }
-
- bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_SYM);
- u32 leb_len=0;
- u8 *bytes = uint_to_uleb128(id, &leb_len);
- bh_buffer_append(&mod->debug_context->op_buffer, bytes, leb_len);
-
- mod->debug_context->last_op_was_rep = 0;
- return id;
-}
-
-static u32 debug_get_file_id(OnyxWasmModule *mod, const char *name) {
- assert(mod && mod->debug_context);
-
- i32 index = shgeti(mod->debug_context->file_info, name);
- if (index == -1) {
- u32 id = mod->debug_context->next_file_id++;
- DebugFileInfo file_info;
- file_info.file_id = id;
-
- bh_arr_each(bh_file_contents, fc, context.loaded_files) {
- if (!strcmp(fc->filename, name)) {
- file_info.line_count = fc->line_count;
- }
- }
- shput(mod->debug_context->file_info, name, file_info);
-
- return id;
- }
-
- return mod->debug_context->file_info[index].value.file_id;
-}
-
-static void debug_set_position(OnyxWasmModule *mod, OnyxToken *token) {
- i32 file_id = debug_get_file_id(mod, token->pos.filename);
-
- bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_SET);
- mod->debug_context->last_op_was_rep = 0;
-
- u32 leb_len=0;
- u8 *bytes = uint_to_uleb128(file_id, &leb_len);
- bh_buffer_append(&mod->debug_context->op_buffer, bytes, leb_len);
-
- bytes = uint_to_uleb128(token->pos.line, &leb_len);
- bh_buffer_append(&mod->debug_context->op_buffer, bytes, leb_len);
-
- mod->debug_context->last_token = token;
-}
-
-// Called for every instruction being emitted
-// This has to emit either:
-// - INC
-// - DEC
-// - REP
-// - SET, REP 0
-static void debug_emit_instruction(OnyxWasmModule *mod, OnyxToken *token) {
- DebugContext *ctx = mod->debug_context;
- assert(ctx && ctx->last_token);
-
- // Sanity check
- if (ctx->last_op_was_rep) {
- assert((ctx->op_buffer.data[ctx->op_buffer.length - 1] & DOT_REP) == DOT_REP);
- }
- i32 file_id, old_file_id;
-
- b32 repeat_previous = 0;
- if (!token || !token->pos.filename) {
- repeat_previous = 1;
-
- } else {
- file_id = debug_get_file_id(mod, token->pos.filename);
- old_file_id = debug_get_file_id(mod, ctx->last_token->pos.filename);
- if (old_file_id == file_id && token->pos.line == ctx->last_token->pos.line) {
- repeat_previous = 1;
- }
- }
-
- if (repeat_previous) {
- // Output / increment REP instruction
- if (ctx->last_op_was_rep) {
- u8 rep_count = ctx->op_buffer.data[ctx->op_buffer.length - 1] & 0b00111111;
- if (rep_count != 63) {
- rep_count += 1;
- ctx->op_buffer.data[ctx->op_buffer.length - 1] = DOT_REP | rep_count;
- return;
- }
- }
-
- bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_REP);
- ctx->last_op_was_rep = 1;
- return;
- }
-
- // At this point, token is definitely non-null.
- ctx->last_op_was_rep = 0;
-
- if (old_file_id == file_id) {
- // We see if we can INC/DEC to get to the line number
- if (ctx->last_token->pos.line < token->pos.line) {
- u32 diff = token->pos.line - ctx->last_token->pos.line;
- if (diff <= 64) {
- bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_INC | (diff - 1));
- goto done;
- }
- }
-
- if (ctx->last_token->pos.line > token->pos.line) {
- u32 diff = ctx->last_token->pos.line - token->pos.line;
- if (diff <= 64) {
- bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_DEC | (diff - 1));
- goto done;
- }
- }
- }
-
- // Otherwise, we need to output a SET, followed by a REP 0,
- // which is what set_position does.
- debug_set_position(mod, token);
- bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_REP);
- ctx->last_op_was_rep = 1;
-
- done:
- ctx->last_token = token;
-}
-
-static void debug_begin_function(OnyxWasmModule *mod, u32 func_idx, OnyxToken *token, char *name) {
- u32 file_id = debug_get_file_id(mod, token->pos.filename);
- u32 line = token->pos.line;
-
- assert(mod->debug_context);
-
- DebugFuncContext func;
- func.func_index = func_idx;
- func.file_id = file_id;
- func.line = line;
- func.op_offset = mod->debug_context->op_buffer.length;
- func.stack_ptr_idx = 0;
- func.name_length = strlen(name);
- func.name = name;
- bh_arr_push(mod->debug_context->funcs, func);
-
- bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_PUSHF);
- debug_set_position(mod, token);
-}
-
-static void debug_end_function(OnyxWasmModule *mod) {
- bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_POPF);
- bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_END);
- mod->debug_context->last_op_was_rep = 0;
- mod->debug_context->last_token = NULL;
-}
-
-static void debug_enter_symbol_frame(OnyxWasmModule *mod) {
- bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_PUSHF);
- mod->debug_context->last_op_was_rep = 0;
-}
-
-static void debug_leave_symbol_frame(OnyxWasmModule *mod) {
- bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_POPF);
- mod->debug_context->last_op_was_rep = 0;
-}
-
-#else
-
-#define debug_introduce_symbol(mod, name, loc, num, type) (void)0
-#define debug_get_file_id(mod, name) (void)0
-#define debug_set_position(mod, token) (void)0
-#define debug_emit_instruction(mod, token) (void)0
-#define debug_begin_function(mod, idx, token, name) (void)0
-#define debug_end_function(mod) (void)0
-#define debug_enter_symbol_frame(mod) (void)0
-#define debug_leave_symbol_frame(mod) (void)0
-
-#endif
-
-
-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,
-};
-
-#ifdef ENABLE_DEBUG_INFO
- #define WI(token, instr) (debug_emit_instruction(mod, token), bh_arr_push(code, ((WasmInstruction){ instr, 0x00 })))
- #define WID(token, instr, data) (debug_emit_instruction(mod, token), bh_arr_push(code, ((WasmInstruction){ instr, data })))
- #define WIL(token, instr, data) (debug_emit_instruction(mod, token), bh_arr_push(code, ((WasmInstruction){ instr, { .l = data } })))
- #define WIP(token, instr, data) (debug_emit_instruction(mod, token), bh_arr_push(code, ((WasmInstruction){ instr, { .p = data } })))
- #define WIR(token, full_instr) (debug_emit_instruction(mod, token), bh_arr_push(code, full_instr))
-#else
- #define WI(token, instr) (bh_arr_push(code, ((WasmInstruction){ instr, 0x00 })))
- #define WID(token, instr, data) (bh_arr_push(code, ((WasmInstruction){ instr, data })))
- #define WIL(token, instr, data) (bh_arr_push(code, ((WasmInstruction){ instr, { .l = data } })))
- #define WIP(token, instr, data) (bh_arr_push(code, ((WasmInstruction){ instr, { .p = data } })))
- #define WIR(token, full_instr) (bh_arr_push(code, full_instr))
-#endif
-
-#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(token, type1, type2) { \
- u64 t0 = local_raw_allocate(mod->local_alloc, type1); \
- u64 t1 = local_raw_allocate(mod->local_alloc, type2); \
- \
- WIL(token, WI_LOCAL_SET, t0); \
- WIL(token, WI_LOCAL_SET, t1); \
- WIL(token, WI_LOCAL_GET, t0); \
- WIL(token, 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 }))
-#define NEXT_DATA_ID(mod) ((u32) bh_arr_length((mod)->data) + 1)
-
-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(data_relocation, u32 data_id);
-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(remove_directive, AstDirectiveRemove* remove);
-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, OnyxToken* block_token);
-EMIT_FUNC_NO_ARGS(leave_structured_block);
-
-static u32 emit_data_entry(OnyxWasmModule *mod, WasmDatum *datum);
-
-static void emit_constexpr(ConstExprContext *ctx, AstTyped *node, u32 offset);
-static b32 emit_constexpr_(ConstExprContext *ctx, AstTyped *node, u32 offset);
-
-#include "wasm_intrinsics.h"
-#include "wasm_type_table.h"
-
-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,
- block->token);
- debug_enter_symbol_frame(mod);
- }
-
- 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);
- debug_leave_symbol_frame(mod);
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(enter_structured_block, StructuredBlockType sbt, OnyxToken* token) {
- 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(token, 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(NULL, 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(jump->token, WI_JUMP, labelidx);
- } else {
- onyx_report_error(jump->token->pos, Error_Critical, "Invalid structured jump.");
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(statement, AstNode* stmt) {
- bh_arr(WasmInstruction) code = *pcode;
-
-#ifdef ENABLE_DEBUG_INFO
- debug_set_position(mod, stmt->token);
-#endif
-
- 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_Remove: emit_remove_directive(mod, &code, (AstDirectiveRemove *) stmt); break;
- case Ast_Kind_Directive_Insert: break;
-
- default: emit_expression(mod, &code, (AstTyped *) stmt); break;
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(local_allocation, AstTyped* stmt) {
- u64 local_idx = local_allocate(mod->local_alloc, stmt);
- bh_imap_put(&mod->local_map, (u64) stmt, local_idx);
-
- if (local_is_wasm_local(stmt)) {
- debug_introduce_symbol(mod, stmt->token, DSL_REGISTER, local_idx, stmt->type);
- } else {
- debug_introduce_symbol(mod, stmt->token, DSL_STACK, local_idx, stmt->type);
- }
-
- if (stmt->kind == Ast_Kind_Local && !(stmt->flags & Ast_Flag_Decl_Followed_By_Init)) {
- bh_arr(WasmInstruction) code = *pcode;
- if (local_is_wasm_local(stmt)) {
- emit_zero_value(mod, &code, onyx_type_to_wasm_type(stmt->type));
- WIL(stmt->token, WI_LOCAL_SET, local_idx);
-
- } else {
- emit_location(mod, &code, stmt);
- WID(stmt->token, WI_I32_CONST, 0);
- WID(stmt->token, WI_I32_CONST, type_size_of(stmt->type));
- if (context.options->use_post_mvp_features) {
- WID(stmt->token, WI_MEMORY_FILL, 0x00);
- } else {
- emit_intrinsic_memory_fill(mod, &code);
- }
- }
-
- *pcode = code;
- }
-
- 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(data_relocation, u32 data_id) {
- bh_arr(WasmInstruction) code = *pcode;
-
- u32 instr_idx = bh_arr_length(code);
- WID(NULL, WI_PTR_CONST, 0);
- assert(mod->current_func_idx >= 0);
-
- DatumPatchInfo patch;
- patch.kind = Datum_Patch_Instruction;
- patch.index = mod->current_func_idx;
- patch.location = instr_idx;
- patch.data_id = data_id;
- patch.offset = 0;
- bh_arr_push(mod->data_patches, patch);
-
- *pcode = code;
-}
-
-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(assign->token, WI_LOCAL_SET, localidx + i);
-
- } else {
- WIL(assign->token, WI_LOCAL_SET, localidx);
- }
-
- *pcode = code;
- return;
- }
- }
-
- if (lval->kind == Ast_Kind_Field_Access) {
- AstFieldAccess* fa = (AstFieldAccess *) lval;
- if (fa->expr->kind == Ast_Kind_Param && type_is_structlike_strict(fa->expr->type)) {
- emit_expression(mod, &code, assign->right);
-
- u64 localidx = bh_imap_get(&mod->local_map, (u64) fa->expr);
- WIL(assign->token, WI_LOCAL_SET, localidx + fa->idx);
-
- *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(assign->token, 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(left->token, WI_LOCAL_SET, lptr_local);
-
- AstArrayLiteral* al = (AstArrayLiteral *) right;
- fori (i, 0, elem_count) {
- WIL(left->token, 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(assign->token, 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(assign->token, WI_LOCAL_SET, expr_tmp);
- u64 offset = 0;
- emit_location_return_offset(mod, &code, lval, &offset);
- WIL(assign->token, 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_Struct) {
- assert(bh_arr_length(type->Struct.linear_members) == 1);
- type = type->Struct.linear_members[0].type;
- }
-
- 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_Distinct) type = type->Distinct.base_type;
- 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(NULL, 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(NULL, WI_I32_STORE_8, ((WasmInstructionData) { alignment, offset }));
- else if (store_size == 2) WID(NULL, WI_I32_STORE_16, ((WasmInstructionData) { alignment, offset }));
- else if (store_size == 4) WID(NULL, WI_I32_STORE, ((WasmInstructionData) { alignment, offset }));
- else if (store_size == 8) WID(NULL, WI_I64_STORE, ((WasmInstructionData) { alignment, offset }));
- } else if (is_basic && (type->Basic.flags & Basic_Flag_Float)) {
- if (store_size == 4) WID(NULL, WI_F32_STORE, ((WasmInstructionData) { alignment, offset }));
- else if (store_size == 8) WID(NULL, WI_F64_STORE, ((WasmInstructionData) { alignment, offset }));
- } else if (is_basic && (type->Basic.flags & Basic_Flag_SIMD)) {
- WID(NULL, WI_V128_STORE, ((WasmInstructionData) { alignment, offset }));
- } else {
- onyx_report_error((OnyxFilePos) { 0 }, Error_Critical,
- "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_Struct) {
- assert(bh_arr_length(type->Struct.linear_members) == 1);
- type = type->Struct.linear_members[0].type;
- }
-
- if (type->kind == Type_Kind_Array) {
- if (offset != 0) {
- WID(NULL, WI_PTR_CONST, offset);
- WI(NULL, WI_PTR_ADD);
- }
-
- *pcode = code;
- return;
- }
-
- if (type->kind == Type_Kind_Enum) type = type->Enum.backing;
- if (type->kind == Type_Kind_Distinct) type = type->Distinct.base_type;
- 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(NULL, instr, ((WasmInstructionData) { alignment, offset }));
-
- if (instr == WI_NOP) {
- onyx_report_error((OnyxFilePos) { 0 }, Error_Critical,
- "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_node->token);
- if (if_node->true_stmt) emit_block(mod, &code, if_node->true_stmt, 0);
-
- if (if_node->false_stmt) {
- WI(if_node->false_stmt->token, 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, while_node->token);
- emit_enter_structured_block(mod, &code, SBT_Continue_Loop, while_node->token);
-
- if (!while_node->bottom_test) {
- emit_expression(mod, &code, while_node->cond);
- WI(NULL, WI_I32_EQZ);
- WID(NULL, WI_COND_JUMP, 0x01);
- }
-
- emit_block(mod, &code, while_node->true_stmt, 0);
-
- if (while_node->bottom_test) {
- emit_expression(mod, &code, while_node->cond);
- WID(while_node->cond->token, WI_COND_JUMP, 0x00);
-
- } else {
- if (bh_arr_last(code).type != WI_JUMP)
- WID(while_node->cond->token, 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, while_node->token);
- emit_enter_structured_block(mod, &code, SBT_Continue_Loop, while_node->token);
-
- emit_block(mod, &code, while_node->true_stmt, 0);
-
- emit_expression(mod, &code, while_node->cond);
- WID(while_node->cond->token, WI_COND_JUMP, 0x00);
-
- emit_leave_structured_block(mod, &code);
- WI(while_node->false_stmt->token, 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
-
- // NOTE: This might not be a range literal
- AstStructLiteral *range = (AstStructLiteral *) for_node->iter;
- 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(for_node->token, WI_LOCAL_SET, step_local);
- WIL(for_node->token, WI_LOCAL_SET, high_local);
- WIL(for_node->token, WI_LOCAL_TEE, low_local);
- WIL(for_node->token, WI_LOCAL_SET, iter_local);
-
- emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
- emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token);
- emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token);
-
- if (range->kind == Ast_Kind_Struct_Literal && (range->args.values[2]->flags & Ast_Flag_Comptime) != 0) {
- AstNumLit *step_value = (AstNumLit *) range->args.values[2];
- assert(step_value->kind == Ast_Kind_NumLit);
-
- if (step_value->value.l >= 0) {
- WIL(for_node->token, WI_LOCAL_GET, iter_local);
- WIL(for_node->token, WI_LOCAL_GET, high_local);
- WI(for_node->token, WI_I32_GE_S);
- WID(for_node->token, WI_COND_JUMP, 0x02);
- } else {
- WIL(for_node->token, WI_LOCAL_GET, iter_local);
- WIL(for_node->token, WI_LOCAL_GET, high_local);
- WI(for_node->token, WI_I32_LT_S);
- WID(for_node->token, WI_COND_JUMP, 0x02);
- }
-
- } else {
- WIL(for_node->token, WI_LOCAL_GET, step_local);
- WID(for_node->token, WI_I32_CONST, 0);
- WI(for_node->token, WI_I32_GE_S);
- WID(for_node->token, WI_IF_START, 0x40);
- WIL(for_node->token, WI_LOCAL_GET, iter_local);
- WIL(for_node->token, WI_LOCAL_GET, high_local);
- WI(for_node->token, WI_I32_GE_S);
- WID(for_node->token, WI_COND_JUMP, 0x03);
- WI(for_node->token, WI_ELSE);
- WIL(for_node->token, WI_LOCAL_GET, iter_local);
- WIL(for_node->token, WI_LOCAL_GET, high_local);
- WI(for_node->token, WI_I32_LT_S);
- WID(for_node->token, WI_COND_JUMP, 0x03);
- WI(for_node->token, WI_IF_END);
- }
-
-
- emit_block(mod, &code, for_node->stmt, 0);
-
- emit_leave_structured_block(mod, &code);
-
- WIL(for_node->token, WI_LOCAL_GET, iter_local);
- WIL(for_node->token, WI_LOCAL_GET, step_local);
- WI(for_node->token, WI_I32_ADD);
- WIL(for_node->token, WI_LOCAL_SET, iter_local);
-
- if (bh_arr_last(code).type != WI_JUMP)
- WID(for_node->token, 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(for_node->token, WI_LOCAL_TEE, ptr_local);
- WIL(for_node->token, WI_PTR_CONST, for_node->iter->type->Array.count * elem_size);
- WI(for_node->token, WI_PTR_ADD);
- WIL(for_node->token, WI_LOCAL_SET, end_ptr_local);
-
- emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
- emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token);
- emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token);
-
- WIL(for_node->token, WI_LOCAL_GET, ptr_local);
- WIL(for_node->token, WI_LOCAL_GET, end_ptr_local);
- WI(for_node->token, WI_PTR_GE);
- WID(for_node->token, WI_COND_JUMP, 0x02);
-
- if (!for_node->by_pointer) {
- if (!it_is_local) emit_local_location(mod, &code, var, &offset);
-
- WIL(for_node->token, 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(for_node->token, WI_LOCAL_SET, iter_local);
- }
-
- emit_block(mod, &code, for_node->stmt, 0);
-
- emit_leave_structured_block(mod, &code);
-
- WIL(for_node->token, WI_LOCAL_GET, ptr_local);
- WIL(for_node->token, WI_PTR_CONST, elem_size);
- WI(for_node->token, WI_PTR_ADD);
- WIL(for_node->token, WI_LOCAL_SET, ptr_local);
-
- if (bh_arr_last(code).type != WI_JUMP)
- WID(for_node->token, 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(for_node->token, WI_LOCAL_SET, end_ptr_local);
- WIL(for_node->token, WI_LOCAL_TEE, ptr_local);
- WIL(for_node->token, WI_LOCAL_GET, end_ptr_local);
- if (elem_size != 1) {
- WID(for_node->token, WI_PTR_CONST, elem_size);
- WI(for_node->token, WI_PTR_MUL);
- }
- WI(for_node->token, WI_PTR_ADD);
- WIL(for_node->token, WI_LOCAL_SET, end_ptr_local);
-
- emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
- emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token);
- emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token);
-
- WIL(for_node->token, WI_LOCAL_GET, ptr_local);
- WIL(for_node->token, WI_LOCAL_GET, end_ptr_local);
- WI(for_node->token, WI_PTR_GE);
- WID(for_node->token, WI_COND_JUMP, 0x02);
-
- if (!for_node->by_pointer) {
- if (!it_is_local) emit_local_location(mod, &code, var, &offset);
-
- WIL(for_node->token, 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(for_node->token, WI_LOCAL_SET, iter_local);
- }
-
- emit_block(mod, &code, for_node->stmt, 0);
-
- emit_leave_structured_block(mod, &code);
-
- WIL(for_node->token, WI_LOCAL_GET, ptr_local);
- WIL(for_node->token, WI_PTR_CONST, elem_size);
- WI(for_node->token, WI_PTR_ADD);
- WIL(for_node->token, WI_LOCAL_SET, ptr_local);
-
- if (bh_arr_last(code).type != WI_JUMP)
- WID(for_node->token, 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_remove_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
- u64 iterator_done_bool = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
- WIL(for_node->token, WI_LOCAL_SET, iterator_remove_func);
- WIL(for_node->token, WI_LOCAL_SET, iterator_close_func);
- WIL(for_node->token, WI_LOCAL_SET, iterator_next_func);
- WIL(for_node->token, WI_LOCAL_SET, iterator_data_ptr);
-
-
- {
- //
- // This pushes an entry onto the stack of for loops that have
- // are iterator that can have a '#remove' directive in them.
- ForRemoveInfo remove_info;
- remove_info.iterator_data_ptr = iterator_data_ptr;
- remove_info.iterator_remove_func = iterator_remove_func;
-
- TypeWithOffset remove_func_type;
- type_linear_member_lookup(for_node->iter->type, 3, &remove_func_type);
- remove_info.remove_func_type_idx = generate_type_idx(mod, remove_func_type.type);
-
- bh_arr_push(mod->for_remove_info, remove_info);
- }
-
- 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, for_node->token);
-
- if (!for_node->no_close) {
- 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, for_node->token);
- emit_enter_structured_block(mod, &code, SBT_Continue_Loop, for_node->token);
-
- if (!it_is_local) emit_local_location(mod, &code, var, &offset);
-
- {
- WIL(for_node->token, WI_LOCAL_GET, iterator_data_ptr);
- WIL(for_node->token, 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(for_node->token, WI_GLOBAL_GET, stack_top_idx);
- WID(for_node->token, WI_PTR_CONST, reserve_size);
- WI(for_node->token, WI_PTR_ADD);
- WID(for_node->token, WI_GLOBAL_SET, stack_top_idx);
-
- i32 type_idx = generate_type_idx(mod, next_func_type.type);
- WID(for_node->token, WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
-
- WID(for_node->token, WI_GLOBAL_GET, stack_top_idx);
- WID(for_node->token, WI_PTR_CONST, reserve_size);
- WI(for_node->token, WI_PTR_SUB);
- WID(for_node->token, WI_GLOBAL_SET, stack_top_idx);
-
- WID(for_node->token, WI_GLOBAL_GET, stack_top_idx);
- emit_load_instruction(mod, &code, return_type, reserve_size - return_size);
- }
-
- WIL(for_node->token, WI_LOCAL_SET, iterator_done_bool);
-
- if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
- else WIL(for_node->token, WI_LOCAL_SET, iter_local);
-
- WIL(for_node->token, WI_LOCAL_GET, iterator_done_bool);
- WI(for_node->token, WI_I32_EQZ);
- WID(for_node->token, WI_COND_JUMP, 0x01);
-
- emit_block(mod, &code, for_node->stmt, 0);
- WID(for_node->token, 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);
-
- bh_arr_pop(mod->for_remove_info);
-
- 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);
-
- debug_enter_symbol_frame(mod);
- debug_introduce_symbol(mod, var->token,
- local_is_wasm_local((AstTyped *) var) ? DSL_REGISTER : DSL_STACK,
- iter_local, var->type);
-
- 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(for_node->token, WI_DROP); WI(for_node->token, WI_DROP); WI(for_node->token, 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, Error_Critical, "Invalid for loop type. You should probably not be seeing this...");
- }
-
- local_free(mod->local_alloc, (AstTyped *) var);
- debug_leave_symbol_frame(mod);
-
- *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, switch_node->token);
-
- 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, (*sc)->block->token);
-
- bh_imap_put(&block_map, (u64) (*sc)->block, block_num);
- block_num++;
- }
-
- switch (switch_node->switch_kind) {
- case Switch_Kind_Integer: {
- 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);
- }
-
- // NOTE: We enter a new block here in order to setup the correct
- // indicies for the jump targets in the branch table. For example,
- //
- // <expr>
- // jump_table
- // label0:
- // ...
- // label1:
- // ...
- //
- // If we didn't enter a new block, then jumping to label 0, would jump
- // to the second block, and so on.
- WID(switch_node->expr->token, WI_BLOCK_START, 0x40);
- emit_expression(mod, &code, switch_node->expr);
- if (switch_node->min_case != 0) {
- WID(switch_node->expr->token, WI_I32_CONST, switch_node->min_case);
- WI(switch_node->expr->token, WI_I32_SUB);
- }
- WIP(switch_node->expr->token, WI_JUMP_TABLE, bt);
- WI(switch_node->expr->token, WI_BLOCK_END);
- break;
- }
-
- case Switch_Kind_Use_Equals: {
- WID(switch_node->expr->token, WI_BLOCK_START, 0x40);
-
- bh_arr_each(CaseToBlock, ctb, switch_node->case_exprs) {
- emit_expression(mod, &code, (AstTyped *) ctb->comparison);
-
- u64 bn = bh_imap_get(&block_map, (u64) ctb->block);
- WID(switch_node->expr->token, WI_IF_START, 0x40);
- WID(switch_node->expr->token, WI_JUMP, bn + 1);
- WI(switch_node->expr->token, WI_IF_END);
- }
-
- WID(switch_node->expr->token, WI_JUMP, block_num);
- WI(switch_node->expr->token, WI_BLOCK_END);
- break;
- }
- }
-
- bh_arr_each(AstSwitchCase *, psc, switch_node->cases) {
- AstSwitchCase *sc = *psc;
- if (bh_imap_get(&block_map, (u64) sc->block) == 0xdeadbeef) continue;
-
- u64 bn = bh_imap_get(&block_map, (u64) sc->block);
-
- // Maybe the Symbol Frame idea should be controlled as a block_flag?
- debug_enter_symbol_frame(mod);
- emit_block(mod, &code, sc->block, 0);
- debug_leave_symbol_frame(mod);
-
- if (bh_arr_last(code).type != WI_JUMP)
- WID(NULL, 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),
- .defer_node= defer,
- .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),
- .defer_node= NULL,
- .instructions = deferred_code,
- .instruction_count = code_count,
- }));
-}
-
-EMIT_FUNC(deferred_stmt, DeferredStmt deferred_stmt) {
- bh_arr(WasmInstruction) code = *pcode;
-
- if (deferred_stmt.defer_node) {
- WI(deferred_stmt.defer_node->token, WI_NOP);
- }
-
- 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) {
- WIR(NULL, 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);
- }
-}
-
-EMIT_FUNC(remove_directive, AstDirectiveRemove* remove) {
- assert(bh_arr_length(mod->for_remove_info) > 0);
-
- bh_arr(WasmInstruction) code = *pcode;
-
- ForRemoveInfo remove_info = bh_arr_last(mod->for_remove_info);
-
- WIL(remove->token, WI_LOCAL_GET, remove_info.iterator_remove_func);
- WIL(remove->token, WI_I32_CONST, mod->null_proc_func_idx);
- WI(remove->token, WI_I32_NE);
- WID(remove->token, WI_IF_START, 0x40);
- WIL(remove->token, WI_LOCAL_GET, remove_info.iterator_data_ptr);
- WIL(remove->token, WI_LOCAL_GET, remove_info.iterator_remove_func);
- WIL(remove->token, WI_CALL_INDIRECT, remove_info.remove_func_type_idx);
- WI(remove->token, WI_IF_END);
-
- *pcode = code;
-}
-
-// 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->token, 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(unop->token, WI_I32_CONST, 0x00);
- emit_expression(mod, &code, unop->expr);
- WI(unop->token, WI_I32_SUB);
-
- }
- else if (type->kind == Basic_Kind_I64) {
- WID(unop->token, WI_I64_CONST, 0x00);
- emit_expression(mod, &code, unop->expr);
- WI(unop->token, WI_I64_SUB);
-
- }
- else {
- emit_expression(mod, &code, unop->expr);
-
- if (type->kind == Basic_Kind_F32) WI(unop->token, WI_F32_NEG);
- if (type->kind == Basic_Kind_F64) WI(unop->token, WI_F64_NEG);
- }
-
- break;
- }
-
- case Unary_Op_Not:
- emit_expression(mod, &code, unop->expr);
-
- WI(unop->token, 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(unop->token, WI_I32_CONST, 0xff);
- WI(unop->token, WI_I32_XOR);
- }
- else if (type->kind == Basic_Kind_I16 || type->kind == Basic_Kind_U16) {
- WID(unop->token, WI_I32_CONST, 0xffff);
- WI(unop->token, WI_I32_XOR);
- }
- else if (type->kind == Basic_Kind_I32 || type->kind == Basic_Kind_U32) {
- WID(unop->token, WI_I32_CONST, 0xffffffff);
- WI(unop->token, WI_I32_XOR);
- }
- else if (type->kind == Basic_Kind_I64 || type->kind == Basic_Kind_U64) {
- WIL(unop->token, WI_I64_CONST, 0xffffffffffffffff);
- WI(unop->token, 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);
-
- OnyxToken* call_token = call->token;
-
- // 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(call_token, WI_GLOBAL_GET, stack_top_idx);
- WIL(call_token, WI_LOCAL_TEE, stack_top_store_local);
- WID(call_token, WI_PTR_CONST, 0); // This will be filled in later.
- WI(call_token, WI_PTR_ADD);
- WID(call_token, 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 (arg->pass_as_any) {
- place_on_stack = 1;
- }
-
- if (place_on_stack) WIL(call_token, 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(call_token, WI_LOCAL_GET, stack_top_store_local);
- WID(call_token, WI_PTR_CONST, reserve_size);
- WI(call_token, 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;
- }
-
- if (arg->pass_as_any) {
- WIL(call_token, WI_I32_CONST, 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(call_token, WI_LOCAL_GET, stack_top_store_local);
- WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
- WID(call_token, WI_PTR_CONST, vararg_any_offsets[i]);
- WI(call_token, WI_PTR_ADD);
- emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], vararg_offset + i * any_size);
-
- WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
- WID(call_token, WI_I32_CONST, vararg_any_types[i]);
- emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Type_Index], vararg_offset + i * any_size + POINTER_SIZE);
-
- reserve_size += any_size;
- }
-
- // fallthrough
- }
-
- case VA_Kind_Typed: {
- WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
- if (vararg_offset > 0) {
- WID(call_token, WI_PTR_CONST, vararg_offset);
- WI(call_token, WI_PTR_ADD);
- }
- WID(call_token, WI_I32_CONST, vararg_count);
- break;
- }
-
- case VA_Kind_Untyped: {
- WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
- WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
- if (vararg_offset > 0) {
- WID(call_token, WI_PTR_CONST, vararg_offset);
- WI(call_token, WI_PTR_ADD);
- }
- emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], reserve_size);
-
- // NOTE: There may be 4 uninitialized bytes here, because pointers are only 4 bytes in WASM.
-
- WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
- WID(call_token, WI_I32_CONST, vararg_count);
- emit_store_instruction(mod, &code, &basic_types[Basic_Kind_I32], reserve_size + POINTER_SIZE);
-
- WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
- if (reserve_size > 0) {
- WID(call_token, WI_PTR_CONST, reserve_size);
- WI(call_token, WI_PTR_ADD);
- }
-
- reserve_size += 4 + POINTER_SIZE;
- 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);
- WIL(NULL, WI_CALL, func_idx);
-
- } else {
- emit_expression(mod, &code, call->callee);
-
- i32 type_idx = generate_type_idx(mod, call->callee->type);
- WID(NULL, WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
- }
-
- if (reserve_size > 0) {
- WIL(call_token, WI_LOCAL_GET, stack_top_store_local);
- WID(call_token, 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(call_token, 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, Error_Critical, \
- "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(call->token, 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, Error_Critical, "SIMD lane instructions expect a compile time lane number."); \
- *pcode = code; \
- return; \
- } \
- WID(call->token, 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, Error_Critical, "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(call->token, 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(call->token, WI_MEMORY_SIZE, 0x00); break;
- case ONYX_INTRINSIC_MEMORY_GROW: WID(call->token, WI_MEMORY_GROW, 0x00); break;
- case ONYX_INTRINSIC_MEMORY_COPY:
- if (context.options->use_post_mvp_features) {
- WIL(call->token, 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(call->token, 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_I32_CLZ: WI(call->token, WI_I32_CLZ); break;
- case ONYX_INTRINSIC_I32_CTZ: WI(call->token, WI_I32_CTZ); break;
- case ONYX_INTRINSIC_I32_POPCNT: WI(call->token, WI_I32_POPCNT); break;
- case ONYX_INTRINSIC_I32_AND: WI(call->token, WI_I32_AND); break;
- case ONYX_INTRINSIC_I32_OR: WI(call->token, WI_I32_OR); break;
- case ONYX_INTRINSIC_I32_XOR: WI(call->token, WI_I32_XOR); break;
- case ONYX_INTRINSIC_I32_SHL: WI(call->token, WI_I32_SHL); break;
- case ONYX_INTRINSIC_I32_SLR: WI(call->token, WI_I32_SHR_U); break;
- case ONYX_INTRINSIC_I32_SAR: WI(call->token, WI_I32_SHR_S); break;
- case ONYX_INTRINSIC_I32_ROTL: WI(call->token, WI_I32_ROTL); break;
- case ONYX_INTRINSIC_I32_ROTR: WI(call->token, WI_I32_ROTR); break;
-
- case ONYX_INTRINSIC_I64_CLZ: WI(call->token, WI_I64_CLZ); break;
- case ONYX_INTRINSIC_I64_CTZ: WI(call->token, WI_I64_CTZ); break;
- case ONYX_INTRINSIC_I64_POPCNT: WI(call->token, WI_I64_POPCNT); break;
- case ONYX_INTRINSIC_I64_AND: WI(call->token, WI_I64_AND); break;
- case ONYX_INTRINSIC_I64_OR: WI(call->token, WI_I64_OR); break;
- case ONYX_INTRINSIC_I64_XOR: WI(call->token, WI_I64_XOR); break;
- case ONYX_INTRINSIC_I64_SHL: WI(call->token, WI_I64_SHL); break;
- case ONYX_INTRINSIC_I64_SLR: WI(call->token, WI_I64_SHR_U); break;
- case ONYX_INTRINSIC_I64_SAR: WI(call->token, WI_I64_SHR_S); break;
- case ONYX_INTRINSIC_I64_ROTL: WI(call->token, WI_I64_ROTL); break;
- case ONYX_INTRINSIC_I64_ROTR: WI(call->token, WI_I64_ROTR); break;
-
- case ONYX_INTRINSIC_F32_ABS: WI(call->token, WI_F32_ABS); break;
- case ONYX_INTRINSIC_F32_CEIL: WI(call->token, WI_F32_CEIL); break;
- case ONYX_INTRINSIC_F32_FLOOR: WI(call->token, WI_F32_FLOOR); break;
- case ONYX_INTRINSIC_F32_TRUNC: WI(call->token, WI_F32_TRUNC); break;
- case ONYX_INTRINSIC_F32_NEAREST: WI(call->token, WI_F32_NEAREST); break;
- case ONYX_INTRINSIC_F32_SQRT: WI(call->token, WI_F32_SQRT); break;
- case ONYX_INTRINSIC_F32_MIN: WI(call->token, WI_F32_MIN); break;
- case ONYX_INTRINSIC_F32_MAX: WI(call->token, WI_F32_MAX); break;
- case ONYX_INTRINSIC_F32_COPYSIGN: WI(call->token, WI_F32_COPYSIGN); break;
-
- case ONYX_INTRINSIC_F64_ABS: WI(call->token, WI_F64_ABS); break;
- case ONYX_INTRINSIC_F64_CEIL: WI(call->token, WI_F64_CEIL); break;
- case ONYX_INTRINSIC_F64_FLOOR: WI(call->token, WI_F64_FLOOR); break;
- case ONYX_INTRINSIC_F64_TRUNC: WI(call->token, WI_F64_TRUNC); break;
- case ONYX_INTRINSIC_F64_NEAREST: WI(call->token, WI_F64_NEAREST); break;
- case ONYX_INTRINSIC_F64_SQRT: WI(call->token, WI_F64_SQRT); break;
- case ONYX_INTRINSIC_F64_MIN: WI(call->token, WI_F64_MIN); break;
- case ONYX_INTRINSIC_F64_MAX: WI(call->token, WI_F64_MAX); break;
- case ONYX_INTRINSIC_F64_COPYSIGN: WI(call->token, 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, Error_Critical,
- "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(call->token, 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, Error_Critical,
- "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(call->token, 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, Error_Critical,
- "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(call->token, 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(call->token, WI_I8X16_SWIZZLE); break;
- case ONYX_INTRINSIC_I8X16_SPLAT: WI(call->token, WI_I8X16_SPLAT); break;
- case ONYX_INTRINSIC_I16X8_SPLAT: WI(call->token, WI_I16X8_SPLAT); break;
- case ONYX_INTRINSIC_I32X4_SPLAT: WI(call->token, WI_I32X4_SPLAT); break;
- case ONYX_INTRINSIC_I64X2_SPLAT: WI(call->token, WI_I64X2_SPLAT); break;
- case ONYX_INTRINSIC_F32X4_SPLAT: WI(call->token, WI_F32X4_SPLAT); break;
- case ONYX_INTRINSIC_F64X2_SPLAT: WI(call->token, WI_F64X2_SPLAT); break;
-
- case ONYX_INTRINSIC_I8X16_EQ: WI(call->token, WI_I8X16_EQ); break;
- case ONYX_INTRINSIC_I8X16_NEQ: WI(call->token, WI_I8X16_NEQ); break;
- case ONYX_INTRINSIC_I8X16_LT_S: WI(call->token, WI_I8X16_LT_S); break;
- case ONYX_INTRINSIC_I8X16_LT_U: WI(call->token, WI_I8X16_LT_U); break;
- case ONYX_INTRINSIC_I8X16_GT_S: WI(call->token, WI_I8X16_GT_S); break;
- case ONYX_INTRINSIC_I8X16_GT_U: WI(call->token, WI_I8X16_GT_U); break;
- case ONYX_INTRINSIC_I8X16_LE_S: WI(call->token, WI_I8X16_LE_S); break;
- case ONYX_INTRINSIC_I8X16_LE_U: WI(call->token, WI_I8X16_LE_U); break;
- case ONYX_INTRINSIC_I8X16_GE_S: WI(call->token, WI_I8X16_GE_S); break;
- case ONYX_INTRINSIC_I8X16_GE_U: WI(call->token, WI_I8X16_GE_U); break;
-
- case ONYX_INTRINSIC_I16X8_EQ: WI(call->token, WI_I16X8_EQ); break;
- case ONYX_INTRINSIC_I16X8_NEQ: WI(call->token, WI_I16X8_NEQ); break;
- case ONYX_INTRINSIC_I16X8_LT_S: WI(call->token, WI_I16X8_LT_S); break;
- case ONYX_INTRINSIC_I16X8_LT_U: WI(call->token, WI_I16X8_LT_U); break;
- case ONYX_INTRINSIC_I16X8_GT_S: WI(call->token, WI_I16X8_GT_S); break;
- case ONYX_INTRINSIC_I16X8_GT_U: WI(call->token, WI_I16X8_GT_U); break;
- case ONYX_INTRINSIC_I16X8_LE_S: WI(call->token, WI_I16X8_LE_S); break;
- case ONYX_INTRINSIC_I16X8_LE_U: WI(call->token, WI_I16X8_LE_U); break;
- case ONYX_INTRINSIC_I16X8_GE_S: WI(call->token, WI_I16X8_GE_S); break;
- case ONYX_INTRINSIC_I16X8_GE_U: WI(call->token, WI_I16X8_GE_U); break;
-
- case ONYX_INTRINSIC_I32X4_EQ: WI(call->token, WI_I32X4_EQ); break;
- case ONYX_INTRINSIC_I32X4_NEQ: WI(call->token, WI_I32X4_NEQ); break;
- case ONYX_INTRINSIC_I32X4_LT_S: WI(call->token, WI_I32X4_LT_S); break;
- case ONYX_INTRINSIC_I32X4_LT_U: WI(call->token, WI_I32X4_LT_U); break;
- case ONYX_INTRINSIC_I32X4_GT_S: WI(call->token, WI_I32X4_GT_S); break;
- case ONYX_INTRINSIC_I32X4_GT_U: WI(call->token, WI_I32X4_GT_U); break;
- case ONYX_INTRINSIC_I32X4_LE_S: WI(call->token, WI_I32X4_LE_S); break;
- case ONYX_INTRINSIC_I32X4_LE_U: WI(call->token, WI_I32X4_LE_U); break;
- case ONYX_INTRINSIC_I32X4_GE_S: WI(call->token, WI_I32X4_GE_S); break;
- case ONYX_INTRINSIC_I32X4_GE_U: WI(call->token, WI_I32X4_GE_U); break;
-
- case ONYX_INTRINSIC_F32X4_EQ: WI(call->token, WI_F32X4_EQ); break;
- case ONYX_INTRINSIC_F32X4_NEQ: WI(call->token, WI_F32X4_NEQ); break;
- case ONYX_INTRINSIC_F32X4_LT: WI(call->token, WI_F32X4_LT); break;
- case ONYX_INTRINSIC_F32X4_GT: WI(call->token, WI_F32X4_GT); break;
- case ONYX_INTRINSIC_F32X4_LE: WI(call->token, WI_F32X4_LE); break;
- case ONYX_INTRINSIC_F32X4_GE: WI(call->token, WI_F32X4_GE); break;
-
- case ONYX_INTRINSIC_F64X2_EQ: WI(call->token, WI_F64X2_EQ); break;
- case ONYX_INTRINSIC_F64X2_NEQ: WI(call->token, WI_F64X2_NEQ); break;
- case ONYX_INTRINSIC_F64X2_LT: WI(call->token, WI_F64X2_LT); break;
- case ONYX_INTRINSIC_F64X2_GT: WI(call->token, WI_F64X2_GT); break;
- case ONYX_INTRINSIC_F64X2_LE: WI(call->token, WI_F64X2_LE); break;
- case ONYX_INTRINSIC_F64X2_GE: WI(call->token, WI_F64X2_GE); break;
-
- case ONYX_INTRINSIC_V128_NOT: WI(call->token, WI_V128_NOT); break;
- case ONYX_INTRINSIC_V128_AND: WI(call->token, WI_V128_AND); break;
- case ONYX_INTRINSIC_V128_ANDNOT: WI(call->token, WI_V128_ANDNOT); break;
- case ONYX_INTRINSIC_V128_OR: WI(call->token, WI_V128_OR); break;
- case ONYX_INTRINSIC_V128_XOR: WI(call->token, WI_V128_XOR); break;
- case ONYX_INTRINSIC_V128_BITSELECT: WI(call->token, WI_V128_BITSELECT); break;
-
- case ONYX_INTRINSIC_I8X16_ABS: WI(call->token, WI_I8X16_ABS); break;
- case ONYX_INTRINSIC_I8X16_NEG: WI(call->token, WI_I8X16_NEG); break;
- case ONYX_INTRINSIC_I8X16_ANY_TRUE: WI(call->token, WI_I8X16_ANY_TRUE); break;
- case ONYX_INTRINSIC_I8X16_ALL_TRUE: WI(call->token, WI_I8X16_ALL_TRUE); break;
- case ONYX_INTRINSIC_I8X16_BITMASK: WI(call->token, WI_I8X16_BITMASK); break;
- case ONYX_INTRINSIC_I8X16_NARROW_I16X8_S: WI(call->token, WI_I8X16_NARROW_I16X8_S); break;
- case ONYX_INTRINSIC_I8X16_NARROW_I16X8_U: WI(call->token, WI_I8X16_NARROW_I16X8_U); break;
- case ONYX_INTRINSIC_I8X16_SHL: WI(call->token, WI_I8X16_SHL); break;
- case ONYX_INTRINSIC_I8X16_SHR_S: WI(call->token, WI_I8X16_SHR_S); break;
- case ONYX_INTRINSIC_I8X16_SHR_U: WI(call->token, WI_I8X16_SHR_U); break;
- case ONYX_INTRINSIC_I8X16_ADD: WI(call->token, WI_I8X16_ADD); break;
- case ONYX_INTRINSIC_I8X16_ADD_SAT_S: WI(call->token, WI_I8X16_ADD_SAT_S); break;
- case ONYX_INTRINSIC_I8X16_ADD_SAT_U: WI(call->token, WI_I8X16_ADD_SAT_U); break;
- case ONYX_INTRINSIC_I8X16_SUB: WI(call->token, WI_I8X16_SUB); break;
- case ONYX_INTRINSIC_I8X16_SUB_SAT_S: WI(call->token, WI_I8X16_SUB_SAT_S); break;
- case ONYX_INTRINSIC_I8X16_SUB_SAT_U: WI(call->token, WI_I8X16_SUB_SAT_U); break;
- case ONYX_INTRINSIC_I8X16_MIN_S: WI(call->token, WI_I8X16_MIN_S); break;
- case ONYX_INTRINSIC_I8X16_MIN_U: WI(call->token, WI_I8X16_MIN_U); break;
- case ONYX_INTRINSIC_I8X16_MAX_S: WI(call->token, WI_I8X16_MAX_S); break;
- case ONYX_INTRINSIC_I8X16_MAX_U: WI(call->token, WI_I8X16_MAX_U); break;
- case ONYX_INTRINSIC_I8X16_AVGR_U: WI(call->token, WI_I8X16_AVGR_U); break;
-
- case ONYX_INTRINSIC_I16X8_ABS: WI(call->token, WI_I16X8_ABS); break;
- case ONYX_INTRINSIC_I16X8_NEG: WI(call->token, WI_I16X8_NEG); break;
- case ONYX_INTRINSIC_I16X8_ANY_TRUE: WI(call->token, WI_I16X8_ANY_TRUE); break;
- case ONYX_INTRINSIC_I16X8_ALL_TRUE: WI(call->token, WI_I16X8_ALL_TRUE); break;
- case ONYX_INTRINSIC_I16X8_BITMASK: WI(call->token, WI_I16X8_BITMASK); break;
- case ONYX_INTRINSIC_I16X8_NARROW_I32X4_S: WI(call->token, WI_I16X8_NARROW_I32X4_S); break;
- case ONYX_INTRINSIC_I16X8_NARROW_I32X4_U: WI(call->token, WI_I16X8_NARROW_I32X4_U); break;
- case ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_S: WI(call->token, WI_I16X8_WIDEN_LOW_I8X16_S); break;
- case ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_S: WI(call->token, WI_I16X8_WIDEN_HIGH_I8X16_S); break;
- case ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_U: WI(call->token, WI_I16X8_WIDEN_LOW_I8X16_U); break;
- case ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_U: WI(call->token, WI_I16X8_WIDEN_HIGH_I8X16_U); break;
- case ONYX_INTRINSIC_I16X8_SHL: WI(call->token, WI_I16X8_SHL); break;
- case ONYX_INTRINSIC_I16X8_SHR_S: WI(call->token, WI_I16X8_SHR_S); break;
- case ONYX_INTRINSIC_I16X8_SHR_U: WI(call->token, WI_I16X8_SHR_U); break;
- case ONYX_INTRINSIC_I16X8_ADD: WI(call->token, WI_I16X8_ADD); break;
- case ONYX_INTRINSIC_I16X8_ADD_SAT_S: WI(call->token, WI_I16X8_ADD_SAT_S); break;
- case ONYX_INTRINSIC_I16X8_ADD_SAT_U: WI(call->token, WI_I16X8_ADD_SAT_U); break;
- case ONYX_INTRINSIC_I16X8_SUB: WI(call->token, WI_I16X8_SUB); break;
- case ONYX_INTRINSIC_I16X8_SUB_SAT_S: WI(call->token, WI_I16X8_SUB_SAT_S); break;
- case ONYX_INTRINSIC_I16X8_SUB_SAT_U: WI(call->token, WI_I16X8_SUB_SAT_U); break;
- case ONYX_INTRINSIC_I16X8_MUL: WI(call->token, WI_I16X8_MUL); break;
- case ONYX_INTRINSIC_I16X8_MIN_S: WI(call->token, WI_I16X8_MIN_S); break;
- case ONYX_INTRINSIC_I16X8_MIN_U: WI(call->token, WI_I16X8_MIN_U); break;
- case ONYX_INTRINSIC_I16X8_MAX_S: WI(call->token, WI_I16X8_MAX_S); break;
- case ONYX_INTRINSIC_I16X8_MAX_U: WI(call->token, WI_I16X8_MAX_U); break;
- case ONYX_INTRINSIC_I16X8_AVGR_U: WI(call->token, WI_I16X8_AVGR_U); break;
-
- case ONYX_INTRINSIC_I32X4_ABS: WI(call->token, WI_I32X4_ABS); break;
- case ONYX_INTRINSIC_I32X4_NEG: WI(call->token, WI_I32X4_NEG); break;
- case ONYX_INTRINSIC_I32X4_ANY_TRUE: WI(call->token, WI_I32X4_ANY_TRUE); break;
- case ONYX_INTRINSIC_I32X4_ALL_TRUE: WI(call->token, WI_I32X4_ALL_TRUE); break;
- case ONYX_INTRINSIC_I32X4_BITMASK: WI(call->token, WI_I32X4_BITMASK); break;
- case ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_S: WI(call->token, WI_I32X4_WIDEN_LOW_I16X8_S); break;
- case ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_S: WI(call->token, WI_I32X4_WIDEN_HIGH_I16X8_S); break;
- case ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_U: WI(call->token, WI_I32X4_WIDEN_LOW_I16X8_U); break;
- case ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_U: WI(call->token, WI_I32X4_WIDEN_HIGH_I16X8_U); break;
- case ONYX_INTRINSIC_I32X4_SHL: WI(call->token, WI_I32X4_SHL); break;
- case ONYX_INTRINSIC_I32X4_SHR_S: WI(call->token, WI_I32X4_SHR_S); break;
- case ONYX_INTRINSIC_I32X4_SHR_U: WI(call->token, WI_I32X4_SHR_U); break;
- case ONYX_INTRINSIC_I32X4_ADD: WI(call->token, WI_I32X4_ADD); break;
- case ONYX_INTRINSIC_I32X4_SUB: WI(call->token, WI_I32X4_SUB); break;
- case ONYX_INTRINSIC_I32X4_MUL: WI(call->token, WI_I32X4_MUL); break;
- case ONYX_INTRINSIC_I32X4_MIN_S: WI(call->token, WI_I32X4_MIN_S); break;
- case ONYX_INTRINSIC_I32X4_MIN_U: WI(call->token, WI_I32X4_MIN_U); break;
- case ONYX_INTRINSIC_I32X4_MAX_S: WI(call->token, WI_I32X4_MAX_S); break;
- case ONYX_INTRINSIC_I32X4_MAX_U: WI(call->token, WI_I32X4_MAX_U); break;
-
- case ONYX_INTRINSIC_I64X2_NEG: WI(call->token, WI_I64X2_NEG); break;
- case ONYX_INTRINSIC_I64X2_SHL: WI(call->token, WI_I64X2_SHL); break;
- case ONYX_INTRINSIC_I64X2_SHR_S: WI(call->token, WI_I64X2_SHR_S); break;
- case ONYX_INTRINSIC_I64X2_SHR_U: WI(call->token, WI_I64X2_SHR_U); break;
- case ONYX_INTRINSIC_I64X2_ADD: WI(call->token, WI_I64X2_ADD); break;
- case ONYX_INTRINSIC_I64X2_SUB: WI(call->token, WI_I64X2_SUB); break;
- case ONYX_INTRINSIC_I64X2_MUL: WI(call->token, WI_I64X2_MUL); break;
-
- case ONYX_INTRINSIC_F32X4_ABS: WI(call->token, WI_F32X4_ABS); break;
- case ONYX_INTRINSIC_F32X4_NEG: WI(call->token, WI_F32X4_NEG); break;
- case ONYX_INTRINSIC_F32X4_SQRT: WI(call->token, WI_F32X4_SQRT); break;
- case ONYX_INTRINSIC_F32X4_ADD: WI(call->token, WI_F32X4_ADD); break;
- case ONYX_INTRINSIC_F32X4_SUB: WI(call->token, WI_F32X4_SUB); break;
- case ONYX_INTRINSIC_F32X4_MUL: WI(call->token, WI_F32X4_MUL); break;
- case ONYX_INTRINSIC_F32X4_DIV: WI(call->token, WI_F32X4_DIV); break;
- case ONYX_INTRINSIC_F32X4_MIN: WI(call->token, WI_F32X4_MIN); break;
- case ONYX_INTRINSIC_F32X4_MAX: WI(call->token, WI_F32X4_MAX); break;
-
- case ONYX_INTRINSIC_F64X2_ABS: WI(call->token, WI_F64X2_ABS); break;
- case ONYX_INTRINSIC_F64X2_NEG: WI(call->token, WI_F64X2_NEG); break;
- case ONYX_INTRINSIC_F64X2_SQRT: WI(call->token, WI_F64X2_SQRT); break;
- case ONYX_INTRINSIC_F64X2_ADD: WI(call->token, WI_F64X2_ADD); break;
- case ONYX_INTRINSIC_F64X2_SUB: WI(call->token, WI_F64X2_SUB); break;
- case ONYX_INTRINSIC_F64X2_MUL: WI(call->token, WI_F64X2_MUL); break;
- case ONYX_INTRINSIC_F64X2_DIV: WI(call->token, WI_F64X2_DIV); break;
- case ONYX_INTRINSIC_F64X2_MIN: WI(call->token, WI_F64X2_MIN); break;
- case ONYX_INTRINSIC_F64X2_MAX: WI(call->token, WI_F64X2_MAX); break;
-
- case ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_S: WI(call->token, WI_I32X4_TRUNC_SAT_F32X4_S); break;
- case ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_U: WI(call->token, WI_I32X4_TRUNC_SAT_F32X4_U); break;
- case ONYX_INTRINSIC_F32X4_CONVERT_I32X4_S: WI(call->token, WI_F32X4_CONVERT_I32X4_S); break;
- case ONYX_INTRINSIC_F32X4_CONVERT_I32X4_U: WI(call->token, WI_F32X4_CONVERT_I32X4_U); break;
-
- case ONYX_INTRINSIC_ATOMIC_WAIT: {
- Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
- emit_intrinsic_atomic_wait(mod, &code, atomic_type, call->token);
- break;
- }
-
- case ONYX_INTRINSIC_ATOMIC_NOTIFY: {
- emit_intrinsic_atomic_notify(mod, &code);
- break;
- }
-
- case ONYX_INTRINSIC_ATOMIC_FENCE: {
- emit_intrinsic_atomic_fence(mod, &code);
- break;
- }
-
- case ONYX_INTRINSIC_ATOMIC_LOAD: {
- Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
- emit_intrinsic_atomic_load(mod, &code, atomic_type, call->token);
- break;
- }
-
- case ONYX_INTRINSIC_ATOMIC_STORE: {
- Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
- emit_intrinsic_atomic_store(mod, &code, atomic_type, call->token);
- break;
- }
-
- case ONYX_INTRINSIC_ATOMIC_ADD: {
- Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
- emit_intrinsic_atomic_add(mod, &code, atomic_type, call->token);
- break;
- }
-
- case ONYX_INTRINSIC_ATOMIC_SUB: {
- Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
- emit_intrinsic_atomic_sub(mod, &code, atomic_type, call->token);
- break;
- }
-
- case ONYX_INTRINSIC_ATOMIC_AND: {
- Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
- emit_intrinsic_atomic_and(mod, &code, atomic_type, call->token);
- break;
- }
-
- case ONYX_INTRINSIC_ATOMIC_OR: {
- Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
- emit_intrinsic_atomic_or(mod, &code, atomic_type, call->token);
- break;
- }
-
- case ONYX_INTRINSIC_ATOMIC_XOR: {
- Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
- emit_intrinsic_atomic_xor(mod, &code, atomic_type, call->token);
- break;
- }
-
- case ONYX_INTRINSIC_ATOMIC_XCHG: {
- Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
- emit_intrinsic_atomic_xchg(mod, &code, atomic_type, call->token);
- break;
- }
-
- case ONYX_INTRINSIC_ATOMIC_CMPXCHG: {
- Type* atomic_type = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
- emit_intrinsic_atomic_cmpxchg(mod, &code, atomic_type, call->token);
- 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(sub->token, WI_PTR_CONST, sub->elem_size);
- WI(sub->token, 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(sub->token, 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;
-
- if (memres->threadlocal) {
- u64 tls_base_idx = bh_imap_get(&mod->index_map, (u64) &builtin_tls_base);
- WID(NULL, WI_PTR_CONST, memres->tls_offset);
- WIL(NULL, WI_GLOBAL_GET, tls_base_idx);
- WI(NULL, WI_PTR_ADD);
-
- } else {
- // :ProperLinking
- assert(memres->data_id != 0);
- emit_data_relocation(mod, &code, memres->data_id);
- }
-
- *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(NULL, WI_LOCAL_GET, local_offset);
-
- } else {
- WIL(NULL, 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(NULL, WI_LOCAL_TEE, tmp_idx);
-
- fori (i, 0, mem_count) {
- type_linear_member_lookup(type, i, &two);
- if (i != 0) WIL(NULL, 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(NULL, 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(NULL, WI_LOCAL_SET, temp_locals[i]);
- }
-
- if (!location_first) WIL(NULL, WI_LOCAL_SET, loc_idx);
-
- fori (i, 0, elem_count) {
- type_linear_member_lookup(type, i, &two);
-
- u64 tmp_idx = temp_locals[i];
- WIL(NULL, WI_LOCAL_GET, loc_idx);
- WIL(NULL, 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(NULL, WI_LOCAL_SET, rptr_local);
- WIL(NULL, WI_LOCAL_SET, lptr_local);
-
- WIL(NULL, WI_LOCAL_GET, rptr_local);
- WID(NULL, WI_I32_CONST, 0);
- WI(NULL, WI_I32_NE);
- emit_enter_structured_block(mod, &code, SBT_Basic_If, NULL);
-
- //
- // CLEANUP: Most of these cases could be much shorter if they used existing intrinsics.
- //
- 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(NULL, WI_LOCAL_GET, lptr_local);
-
- WIL(NULL, 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(NULL, WI_LOCAL_GET, lptr_local);
-
- if (offset != 0) {
- WIL(NULL, WI_PTR_CONST, offset);
- WI(NULL, WI_PTR_ADD);
- }
-
- WIL(NULL, WI_LOCAL_GET, rptr_local);
- WIL(NULL, WI_I32_CONST, elem_count * elem_size);
- WI(NULL, 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(NULL, WI_PTR_CONST, 0);
- WIL(NULL, WI_LOCAL_SET, offset_local);
-
- WID(NULL, WI_BLOCK_START, 0x40);
- WID(NULL, WI_LOOP_START, 0x40);
- WIL(NULL, WI_LOCAL_GET, offset_local);
- WIL(NULL, WI_LOCAL_GET, lptr_local);
- WI(NULL, WI_PTR_ADD);
-
- WIL(NULL, WI_LOCAL_GET, offset_local);
- WIL(NULL, WI_LOCAL_GET, rptr_local);
- WI(NULL, WI_PTR_ADD);
-
- emit_load_instruction(mod, &code, elem_type, 0);
- emit_store_instruction(mod, &code, elem_type, offset);
-
- WIL(NULL, WI_LOCAL_GET, offset_local);
- WIL(NULL, WI_PTR_CONST, elem_size);
- WI(NULL, WI_PTR_ADD);
- WIL(NULL, WI_LOCAL_TEE, offset_local);
-
- WIL(NULL, WI_PTR_CONST, elem_count * elem_size);
- WI(NULL, WI_PTR_GE);
- WID(NULL, WI_COND_JUMP, 0x01);
-
- WID(NULL, WI_JUMP, 0x00);
-
- WI(NULL, WI_LOOP_END);
- WI(NULL, WI_BLOCK_END);
-
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
- }
-
- WI(NULL, WI_ELSE);
-
- { // If the source ptr is null (0), then just copy in 0 bytes.
- WIL(NULL, WI_LOCAL_GET, lptr_local);
- if (offset != 0) {
- WIL(NULL, WI_PTR_CONST, offset);
- WI(NULL, WI_PTR_ADD);
- }
-
- WIL(NULL, WI_I32_CONST, 0);
-
- WIL(NULL, WI_I32_CONST, elem_count * elem_size);
-
- if (context.options->use_post_mvp_features) {
- WI(NULL, WI_MEMORY_FILL);
- } else {
- emit_intrinsic_memory_fill(mod, &code);
- }
- }
-
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-
- emit_leave_structured_block(mod, &code);
- *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(al->token, 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(al->token, WI_LOCAL_GET, mod->stack_base_idx);
- WIL(al->token, WI_PTR_CONST, local_offset);
- WI(al->token, 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_expr->token);
- 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(if_expr->token, WI_LOCAL_SET, result_local);
-
- offset = 0;
- WI(if_expr->token, 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(if_expr->token, 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(if_expr->token, 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(doblock->block->token, 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;
-
- expr = (AstTyped *) strip_aliases((AstNode *) expr);
-
- switch (expr->kind) {
- case Ast_Kind_Param:
- case Ast_Kind_Local:
- case Ast_Kind_Array_Literal:
- case Ast_Kind_Struct_Literal: {
- 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;
- emit_memory_reservation_location(mod, &code, memres);
- *offset_return = 0;
- break;
- }
-
- default: {
- onyx_report_error(expr->token->pos, Error_Critical, "Unable to generate location 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(NULL, WI_PTR_CONST, offset);
- WI(NULL, 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->flags & Ast_Flag_Expr_Ignored) return;
-
- if (type->type_id != 0) {
- WID(NULL, WI_I32_CONST, ((AstType *) expr)->type_id);
- } else {
- Type* t = type_build_from_ast(context.ast_alloc, type);
- WID(NULL, 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(NULL, WI_LOCAL_GET, localidx + idx);
-
- } else {
- assert(localidx & LOCAL_IS_WASM);
- WIL(NULL, WI_LOCAL_GET, localidx);
- }
- break;
- }
-
- case Param_Pass_By_Implicit_Pointer: {
- assert(localidx & LOCAL_IS_WASM);
- WIL(NULL, 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(NULL, 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(NULL, WI_PTR_CONST, offset);
- WI(NULL, WI_PTR_ADD);
- }
- }
-
- break;
- }
-
- case Ast_Kind_Global: {
- i32 globalidx = (i32) bh_imap_get(&mod->index_map, (u64) expr);
-
- WID(NULL, 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;
- }
-
- WIR(NULL, instr);
- break;
- }
-
- case Ast_Kind_StrLit: {
- // :ProperLinking
- AstStrLit *strlit = (AstStrLit *) expr;
- assert(strlit->data_id > 0);
- emit_data_relocation(mod, &code, strlit->data_id);
-
- if (strlit->is_cstr == 0)
- WID(NULL, WI_I32_CONST, strlit->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(NULL, 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;
-
- if (node_is_addressable_literal((AstNode *) aof->expr)) {
- aof->expr->flags |= Ast_Flag_Decl_Followed_By_Init;
- aof->expr->flags |= Ast_Flag_Address_Taken;
- emit_local_allocation(mod, &code, aof->expr);
- emit_location(mod, &code, aof->expr);
- emit_expression(mod, &code, aof->expr);
- emit_store_instruction(mod, &code, aof->expr->type, 0);
- }
-
- 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;
- assert(localidx & LOCAL_IS_WASM);
- WIL(NULL, 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(NULL, 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(NULL, 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(NULL, WI_LOCAL_SET, temporaries[i]);
- }
-
- fori (i, 0, idx) WI(NULL, WI_DROP);
-
- fori (i, 0, field_linear_members) {
- type_linear_member_lookup(field->type, i, &two);
-
- WIL(NULL, 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(NULL, WI_DROP);
- WIL(NULL, WI_LOCAL_SET, hi_local);
- WIL(NULL, WI_LOCAL_TEE, lo_local);
- if (sl->elem_size != 1) {
- WID(NULL, WI_I32_CONST, sl->elem_size);
- WI(NULL, WI_I32_MUL);
- }
- emit_expression(mod, &code, sl->addr);
- WI(NULL, WI_I32_ADD);
- WIL(NULL, WI_LOCAL_GET, hi_local);
- WIL(NULL, WI_LOCAL_GET, lo_local);
- WI(NULL, 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(NULL, WI_I32_CONST, so->size);
- break;
- }
-
- case Ast_Kind_Align_Of: {
- AstAlignOf* ao = (AstAlignOf *) expr;
- WID(NULL, WI_I32_CONST, ao->alignment);
- break;
- }
-
- case Ast_Kind_Enum_Value: {
- AstEnumValue* ev = (AstEnumValue *) expr;
- AstNumLit * num = (AstNumLit *) ev->value;
- assert(num->kind == Ast_Kind_NumLit);
-
- WasmType backing_type = onyx_type_to_wasm_type(ev->type);
- if (backing_type == WASM_TYPE_INT32) WID(NULL, WI_I32_CONST, num->value.i);
- else if (backing_type == WASM_TYPE_INT64) WID(NULL, WI_I64_CONST, num->value.l);
- else onyx_report_error(ev->token->pos, Error_Critical, "Invalid backing type for enum.");
- break;
- }
-
- case Ast_Kind_Memres: {
- AstMemRes* memres = (AstMemRes *) expr;
- emit_memory_reservation_location(mod, &code, memres);
- emit_load_instruction(mod, &code, memres->type, 0);
- break;
- }
-
- case Ast_Kind_File_Contents: {
- AstFileContents* fc = (AstFileContents *) expr;
-
- assert(fc->data_id > 0);
- assert(fc->size > 0);
-
- // :ProperLinking
- emit_data_relocation(mod, &code, fc->data_id);
- WID(NULL, 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;
- }
-
- case Ast_Kind_Switch_Case: {
- // This error message should be moved to checking, but this is the
- // best place to do it right now.
- onyx_report_error(expr->token->pos, Error_Critical, "'case' statements are only allowed in a 'switch' statement.");
- break;
- }
-
- case Ast_Kind_Code_Block: {
- // Like above, this error message should be moved to checking, but
- // this is the best place to do it right now.
- onyx_report_error(expr->token->pos, Error_Critical, "'#quote' blocks are only to be used at compile-time. Using them as a runtime value is not allowed.");
- break;
- }
-
- case Ast_Kind_Foreign_Block: {
- AstForeignBlock *fb = (AstForeignBlock *) expr;
- WID(NULL, WI_I32_CONST, fb->foreign_block_number);
- break;
- }
-
- case Ast_Kind_Zero_Value: {
- AstZeroValue *zv = (AstZeroValue *) expr;
- assert(zv->type);
- emit_zero_value_for_type(mod, &code, zv->type, zv->token);
- 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(NULL, 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_I32_FROM_I64, 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_I32_FROM_I64, 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(NULL, WI_DROP);
- *pcode = code;
- return;
- }
-
- if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
- WID(NULL, WI_I32_CONST, from->Array.count);
- *pcode = code;
- return;
- }
-
- if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_DynArray) {
- WI(NULL, WI_DROP);
- WI(NULL, WI_DROP);
- WI(NULL, WI_DROP);
- *pcode = code;
- return;
- }
-
- if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Slice) {
- // Nothing needs to be done because they are identical
- *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;
- }
-
- if (to->kind == Type_Kind_Distinct || from->kind == Type_Kind_Distinct) {
- // 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(NULL, 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(NULL, WI_LOCAL_SET, dest_loc);
-
- } else if (mod->curr_cc == CC_Return_Stack) {
- WIL(NULL, WI_LOCAL_GET, mod->stack_base_idx);
- WID(NULL, WI_I32_CONST, type_size_of(ret->expr->type));
- WI(NULL, 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);
-
- i64 jump_label = get_structured_jump_label(mod, Jump_Type_Return, 1);
- if (jump_label >= 0) {
- WIL(NULL, WI_JUMP, jump_label);
-
- } else {
- // 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--;
- }
- }
-
- // Make a patch for the two instructions needed to restore the stack pointer
- SUBMIT_PATCH(mod->stack_leave_patches, 0);
- WI(NULL, WI_NOP);
- WI(NULL, WI_NOP);
-
- WI(NULL, 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(NULL, WI_I32_CONST, 0); break;
- case WASM_TYPE_INT64: WIL(NULL, WI_I64_CONST, 0); break;
- case WASM_TYPE_FLOAT32: WIL(NULL, WI_F32_CONST, 0); break;
- case WASM_TYPE_FLOAT64: WIL(NULL, 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(NULL, 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(NULL, 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, Error_Critical, "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);
- if (ft->Function.return_type->kind == Type_Kind_Struct && !type_is_compound(ft->Function.return_type)) {
- return_type = onyx_type_to_wasm_type(ft->Function.return_type->Struct.linear_members[0].type);
- }
-
- *(t++) = (char) return_type;
- *t = '\0';
-
- i32 type_idx = 0;
- i32 index = shgeti(mod->type_map, type_repr_buf);
- if (index != -1) {
- type_idx = mod->type_map[index].value;
- } 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;
-
- fori (i, 0, type->param_count) {
- type->param_types[i] = type_repr_buf[i];
- }
-
- bh_arr_push(mod->types, type);
-
- shput(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 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 = { 0 };
- wasm_func.type_idx = type_idx;
- wasm_func.location = fd->token;
-
- bh_arr_new(mod->allocator, wasm_func.code, 16);
-
- i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) fd);
- mod->current_func_idx = func_idx;
-
- debug_begin_function(mod, func_idx, fd->token, get_function_name(fd));
-
- if (fd == builtin_initialize_data_segments && context.options->use_post_mvp_features) {
- emit_initialize_data_segments_body(mod, &wasm_func.code);
-
- debug_emit_instruction(mod, NULL);
- 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);
- mod->current_func_idx = -1;
-
- debug_end_function(mod);
- return;
- }
-
- if (fd == builtin_run_init_procedures) {
- emit_run_init_procedures(mod, &wasm_func.code);
-
- debug_emit_instruction(mod, NULL);
- 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);
- mod->current_func_idx = -1;
-
- debug_end_function(mod);
- return;
- }
-
- if (fd->body != NULL) {
- mod->local_alloc = &wasm_func.locals;
-
- // 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)) {
- debug_introduce_symbol(mod, param->local->token, DSL_REGISTER, localidx | LOCAL_IS_WASM, 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: {
- debug_introduce_symbol(mod, param->local->token, DSL_REGISTER, localidx | LOCAL_IS_WASM, param->local->type);
- bh_imap_put(&mod->local_map, (u64) param->local, localidx++ | LOCAL_IS_WASM);
- break;
- }
-
- default: assert(0);
- }
- }
-
- 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);
-
- debug_emit_instruction(mod, fd->token);
- debug_emit_instruction(mod, fd->token);
- debug_emit_instruction(mod, fd->token);
- debug_emit_instruction(mod, fd->token);
- debug_emit_instruction(mod, fd->token);
- bh_arr_insert_end(wasm_func.code, 5);
- fori (i, 0, 5) wasm_func.code[i] = (WasmInstruction) { WI_NOP, 0 };
-
- // TODO: Emit debug info for the above instructions
-
- 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.
- debug_emit_instruction(mod, NULL);
- debug_emit_instruction(mod, NULL);
-
- 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);
-
- if (fd->closing_brace) {
- debug_emit_instruction(mod, fd->closing_brace);
- bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_NOP, 0x00 }));
- }
-
- debug_emit_instruction(mod, NULL);
- bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
-
- bh_imap_clear(&mod->local_map);
-
- bh_arr_set_at(mod->funcs, func_idx - mod->foreign_function_count, wasm_func);
- mod->current_func_idx = -1;
-
- debug_end_function(mod);
-}
-
-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 = bh_aprintf(global_heap_allocator, "%b", fd->foreign_module->text, fd->foreign_module->length),
- .name = bh_aprintf(global_heap_allocator, "%b", fd->foreign_name->text, fd->foreign_name->length),
- };
-
- 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;
- }
-
- shput(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, glob);
-
- if (global == &builtin_stack_top)
- module->stack_top_ptr = &module->globals[global_idx].initial_value[0].data.i1;
-
- if (global == &builtin_heap_start)
- module->heap_start_ptr = &module->globals[global_idx].initial_value[0].data.i1;
-
- if (global == &builtin_tls_size)
- module->globals[global_idx].initial_value[0].data.i1 = module->next_tls_offset;
-}
-
-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);
-
- i32 index = shgeti(mod->string_literals, (char *) strdata);
- if (index != -1) {
- StrLitInfo sti = mod->string_literals[index].value;
- strlit->data_id = sti.data_id;
- strlit->length = sti.len;
-
- bh_free(global_heap_allocator, strdata);
- return;
- }
-
- // :ProperLinking
- u32 actual_length = length + (strlit->is_cstr ? 1 : 0);
- WasmDatum datum = {
- .alignment = 1,
- .length = actual_length,
- .data = strdata,
- };
-
- strlit->data_id = emit_data_entry(mod, &datum);
- strlit->length = length;
-
- // :ProperLinking
- shput(mod->string_literals, (char *) strdata, ((StrLitInfo) { strlit->data_id, strlit->length }));
-}
-
-static u32 emit_data_entry(OnyxWasmModule *mod, WasmDatum *datum) {
- datum->offset_ = 0;
- datum->id = NEXT_DATA_ID(mod);
- bh_arr_push(mod->data, *datum);
- return datum->id;
-}
-
-static void emit_constexpr(ConstExprContext *ctx, AstTyped *node, u32 offset) {
- if (!emit_constexpr_(ctx, node, offset)) {
- onyx_report_error(node->token->pos, Error_Critical,
- "Cannot generate constant data for '%s'.",
- onyx_ast_node_kind_string(node->kind));
- }
-}
-
-static b32 emit_constexpr_(ConstExprContext *ctx, AstTyped *node, u32 offset) {
-#define CE(type, off) (*((type *) bh_pointer_add(ctx->data, offset + (off))))
- assert(ctx->data_id);
- assert(ctx->data);
-
- b32 retval = 1;
- node = (AstTyped *) strip_aliases((AstNode *) node);
-
- if (node_is_type((AstNode *) node)) {
- Type* constructed_type = type_build_from_ast(context.ast_alloc, (AstType *) node);
- CE(i32, 0) = constructed_type->id;
- return 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_constexpr_(ctx, *expr, i * elem_size + offset);
- 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_constexpr_(ctx, sl->args.values[i], smem.offset + offset);
- }
-
- break;
- }
-
- case Ast_Kind_StrLit: {
- AstStrLit* sl = (AstStrLit *) node;
-
- // NOTE: This assumes the data_id and the length fields have been filled out
- // by emit_string_literal.
- if (POINTER_SIZE == 4) {
- CE(u32, 0) = 0;
- CE(u32, 4) = sl->length;
- } else {
- CE(u64, 0) = 0;
- CE(u64, 8) = sl->length;
- }
-
- assert(sl->data_id > 0);
-
- DatumPatchInfo patch;
- patch.kind = Datum_Patch_Data;
- patch.index = ctx->data_id;
- patch.location = offset;
- patch.data_id = sl->data_id;
- patch.offset = 0;
- bh_arr_push(ctx->module->data_patches, patch);
-
- break;
- }
-
- case Ast_Kind_Enum_Value: {
- AstEnumValue* ev = (AstEnumValue *) node;
- retval &= emit_constexpr_(ctx, (AstTyped *) ev->value, offset);
- break;
- }
-
- case Ast_Kind_Function: {
- AstFunction* func = (AstFunction *) node;
- CE(u32, 0) = get_element_idx(ctx->module, func);
- break;
- }
-
- case Ast_Kind_Size_Of: {
- AstSizeOf* so = (AstSizeOf *) node;
- CE(u32, 0) = so->size;
- break;
- }
-
- case Ast_Kind_Align_Of: {
- AstAlignOf* ao = (AstAlignOf *) node;
- CE(u32, 0) = ao->alignment;
- break;
- }
-
- case Ast_Kind_Zero_Value: {
- memset(bh_pointer_add(ctx->data, offset), 0, type_size_of(node->type));
- 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:
- CE(i8, 0) = (i8) ((AstNumLit *) node)->value.i;
- return retval;
-
- case Basic_Kind_I16:
- case Basic_Kind_U16:
- CE(i16, 0) = (i16) ((AstNumLit *) node)->value.i;
- return retval;
-
- case Basic_Kind_I32:
- case Basic_Kind_U32:
- case Basic_Kind_Rawptr:
- CE(i32, 0) = ((AstNumLit *) node)->value.i;
- return retval;
-
- case Basic_Kind_I64:
- case Basic_Kind_U64:
- CE(i64, 0) = ((AstNumLit *) node)->value.l;
- return retval;
-
- case Basic_Kind_F32:
- CE(f32, 0) = ((AstNumLit *) node)->value.f;
- return retval;
-
- case Basic_Kind_F64:
- CE(f64, 0) = ((AstNumLit *) node)->value.d;
- return retval;
-
- default: break;
- }
-
- //fallthrough
- }
-
- case Ast_Kind_Code_Block: break;
-
- default: retval = 0;
- }
-
- return retval;
-
-#undef CE
-}
-
-static void emit_memory_reservation(OnyxWasmModule* mod, AstMemRes* memres) {
- // :ProperLinking
- 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->data_id = table_location;
- return;
- }
-
- if (foreign_blocks_node != NULL && (AstMemRes *) foreign_blocks_node == memres) {
- u64 foreign_blocks_location = build_foreign_blocks(mod);
- memres->data_id = foreign_blocks_location;
- return;
- }
-
- if (tagged_procedures_node != NULL && (AstMemRes *) tagged_procedures_node == memres) {
- u64 tagged_procedures_location = build_tagged_procedures(mod);
- memres->data_id = tagged_procedures_location;
- return;
- }
-
- if (memres->threadlocal) {
- memres->tls_offset = mod->next_tls_offset;
- bh_align(memres->tls_offset, alignment);
- mod->next_tls_offset = memres->tls_offset + size;
-
- } else {
- // :ProperLinking
- u8* data = NULL;
- if (memres->initial_value != NULL) {
- assert(!memres->threadlocal);
- data = bh_alloc(global_heap_allocator, size);
- }
-
- WasmDatum datum = {
- .alignment = alignment,
- .length = size,
- .data = data,
- };
- memres->data_id = emit_data_entry(mod, &datum);
-
- if (memres->initial_value != NULL) {
- ConstExprContext constexpr_ctx;
- constexpr_ctx.module = mod;
- constexpr_ctx.data = data;
- constexpr_ctx.data_id = memres->data_id;
- emit_constexpr(&constexpr_ctx, memres->initial_value, 0);
- }
- }
-}
-
-static void emit_file_contents(OnyxWasmModule* mod, AstFileContents* fc) {
- // 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);
-
- OnyxToken *filename_token = fc->filename_expr->token;
-
- token_toggle_end(filename_token);
- char* temp_fn = bh_alloc_array(global_scratch_allocator, char, filename_token->length);
- i32 temp_fn_len = string_process_escape_seqs(temp_fn, filename_token->text, filename_token->length);
- char* filename = bh_lookup_file(temp_fn, parent_folder, "", 0, NULL, 0);
- fc->filename = bh_strdup(global_heap_allocator, filename);
- token_toggle_end(filename_token);
- }
-
- i32 index = shgeti(mod->loaded_file_info, fc->filename);
- if (index != -1) {
- StrLitInfo info = mod->loaded_file_info[index].value;
- fc->data_id = info.data_id;
- fc->size = info.len;
- return;
- }
-
- if (!bh_file_exists(fc->filename)) {
- onyx_report_error(fc->token->pos, Error_Critical,
- "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);
-
- WasmDatum datum = {
- .alignment = 16,
- .length = length,
- .data = actual_data,
- };
- fc->data_id = emit_data_entry(mod, &datum);
- fc->size = length - 1;
-
- shput(mod->loaded_file_info, fc->filename, ((StrLitInfo) {
- .data_id = fc->data_id,
- .len = fc->size,
- }));
-}
-
-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,
- .data_patches = NULL,
-
- .next_tls_offset = 0,
- .tls_size_ptr = NULL,
-
- .elems = NULL,
- .next_elem_idx = 0,
-
- .needs_memory_section = 0,
- .memory_min_size = 0,
- .memory_max_size = 0,
-
- .structured_jump_target = NULL,
- .return_location_stack = NULL,
- .local_allocations = NULL,
- .stack_leave_patches = NULL,
- .deferred_stmts = NULL,
-
- .heap_start_ptr = NULL,
-
- .stack_top_ptr = NULL,
- .stack_base_idx = 0,
-
- .foreign_function_count = 0,
-
- .null_proc_func_idx = -1,
-
- .libraries = NULL,
- .library_paths = NULL,
-
- .foreign_blocks = NULL,
- .next_foreign_block_idx = 0,
-
- .procedures_with_tags = NULL
- };
-
- 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(alloc, module.libraries, 4);
- bh_arr_new(alloc, module.library_paths, 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);
-
- sh_new_arena(module.type_map);
- sh_new_arena(module.exports);
- sh_new_arena(module.loaded_file_info);
- sh_new_arena(module.string_literals);
-
- 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);
- bh_arr_new(global_heap_allocator, module.foreign_blocks, 4);
- bh_arr_new(global_heap_allocator, module.procedures_with_tags, 4);
- bh_arr_new(global_heap_allocator, module.data_patches, 4);
-
-#ifdef ENABLE_DEBUG_INFO
- module.debug_context = bh_alloc_item(context.ast_alloc, DebugContext);
- module.debug_context->allocator = global_heap_allocator;
- module.debug_context->next_file_id = 0;
- module.debug_context->next_sym_id = 0;
- module.debug_context->last_token = NULL;
-
- sh_new_arena(module.debug_context->file_info);
- bh_arr_new(global_heap_allocator, module.debug_context->sym_info, 32);
- bh_arr_new(global_heap_allocator, module.debug_context->sym_patches, 32);
- bh_arr_new(global_heap_allocator, module.debug_context->funcs, 16);
-
- bh_buffer_init(&module.debug_context->op_buffer, global_heap_allocator, 1024);
-#endif
-
- return module;
-}
-
-void emit_entity(Entity* ent) {
- OnyxWasmModule* module = context.wasm_module;
- module->current_func_idx = -1;
-
- 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);
- }
-
- if (ent->function->tags != NULL) {
- bh_arr_push(module->procedures_with_tags, ent->function);
- }
- break;
-
- 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);
- }
-
- if (ent->expr->kind == Ast_Kind_Directive_Library) {
- bh_arr_push(module->libraries, ent->library->library_name);
- }
- break;
- }
-
- case Entity_Type_Foreign_Block: {
- ent->foreign_block->foreign_block_number = module->next_foreign_block_idx++;
- bh_arr_push(module->foreign_blocks, (AstForeignBlock *) ent->foreign_block);
- 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_link(OnyxWasmModule *module, OnyxWasmLinkOptions *options) {
- // If the pointer size is going to change,
- // the code will probably need to be altered.
- assert(POINTER_SIZE == 4);
-
- module->memory_min_size = options->memory_min_size;
- module->memory_max_size = options->memory_max_size;
-
- if (context.options->use_multi_threading || options->import_memory) {
- module->needs_memory_section = 0;
-
- WasmImport mem_import = {
- .kind = WASM_FOREIGN_MEMORY,
- .min = options->memory_min_size,
- .max = options->memory_max_size, // NOTE: Why not use all 4 Gigs of memory?
- .shared = context.options->runtime == Runtime_Js,
-
- .mod = options->import_memory_module_name,
- .name = options->import_memory_import_name,
- };
-
- bh_arr_push(module->imports, mem_import);
-
- } else {
- module->needs_memory_section = 1;
- }
-
- if (options->export_memory) {
- WasmExport mem_export = {
- .kind = WASM_FOREIGN_MEMORY,
- .idx = 0,
- };
-
- shput(module->exports, options->export_memory_name, mem_export);
- module->export_count++;
- }
-
- if (options->export_func_table) {
- WasmExport func_table_export = {
- .kind = WASM_FOREIGN_TABLE,
- .idx = 0,
- };
-
- shput(module->exports, options->export_func_table_name, func_table_export);
- module->export_count++;
- }
-
- u32 datum_offset = options->null_reserve_size;
- bh_arr_each(WasmDatum, datum, module->data) {
- assert(datum->id > 0);
-
- bh_align(datum_offset, datum->alignment);
- datum->offset_ = datum_offset;
-
- datum_offset += datum->length;
- }
-
- bh_arr_each(DatumPatchInfo, patch, module->data_patches) {
- assert(patch->data_id > 0);
- WasmDatum *datum = &module->data[patch->data_id - 1];
- assert(datum->id == patch->data_id);
-
- switch (patch->kind) {
- case Datum_Patch_Instruction: {
- WasmFunc *func = &module->funcs[patch->index - module->foreign_function_count];
-
- assert(func->code[patch->location].type == WI_PTR_CONST);
- func->code[patch->location].data.l = (u64) datum->offset_ + patch->offset;
- break;
- }
-
- case Datum_Patch_Data: {
- WasmDatum *datum_to_alter = &module->data[patch->index - 1];
- assert(datum_to_alter->id == patch->index);
-
- *((u32 *) bh_pointer_add(datum_to_alter->data, patch->location)) = (u32) datum->offset_ + patch->offset;
- break;
- }
-
- case Datum_Patch_Relative: {
- WasmDatum *datum_to_alter = &module->data[patch->index - 1];
- assert(datum_to_alter->id == patch->index);
-
- u32 *addr = (u32 *) bh_pointer_add(datum_to_alter->data, patch->location);
- if (*addr != 0) {
- *addr += (u32) datum->offset_ + patch->offset;
- }
-
- break;
- }
-
- default: assert(0);
- }
- }
-
- assert(module->stack_top_ptr && module->heap_start_ptr);
-
- *module->stack_top_ptr = datum_offset;
- bh_align(*module->stack_top_ptr, options->stack_alignment);
-
- *module->heap_start_ptr = *module->stack_top_ptr + options->stack_size;
- bh_align(*module->heap_start_ptr, 16);
-}
-
-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);
- shfree(module->type_map);
- shfree(module->exports);
-}
-
-
-b32 onyx_wasm_build_link_options_from_node(OnyxWasmLinkOptions *opts, AstTyped *node) {
- node = (AstTyped *) strip_aliases((AstNode *) node);
-
- assert(node && node->kind == Ast_Kind_Struct_Literal);
- assert(builtin_link_options_type);
-
- Type *link_options_type = type_build_from_ast(context.ast_alloc, builtin_link_options_type);
-
- AstStructLiteral *input = (AstStructLiteral *) node;
-
- StructMember smem;
- b32 out_is_valid;
-
- // TODO: These should be properly error handled.
- assert(type_lookup_member(link_options_type, "stack_first", &smem));
- opts->stack_first = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid) != 0;
- if (!out_is_valid) return 0;
-
- assert(type_lookup_member(link_options_type, "stack_size", &smem));
- opts->stack_size = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
- if (!out_is_valid) return 0;
-
- assert(type_lookup_member(link_options_type, "stack_alignment", &smem));
- opts->stack_alignment = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
- if (!out_is_valid) return 0;
-
- assert(type_lookup_member(link_options_type, "null_reserve_size", &smem));
- opts->null_reserve_size = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
- if (!out_is_valid) return 0;
-
- assert(type_lookup_member(link_options_type, "import_memory", &smem));
- opts->import_memory = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid) != 0;
- if (!out_is_valid) return 0;
-
- assert(type_lookup_member(link_options_type, "import_memory_module_name", &smem));
- opts->import_memory_module_name = get_expression_string_value(input->args.values[smem.idx], &out_is_valid);
- if (!out_is_valid) return 0;
-
- assert(type_lookup_member(link_options_type, "import_memory_import_name", &smem));
- opts->import_memory_import_name = get_expression_string_value(input->args.values[smem.idx], &out_is_valid);
- if (!out_is_valid) return 0;
-
- assert(type_lookup_member(link_options_type, "export_memory", &smem));
- opts->export_memory = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid) != 0;
- if (!out_is_valid) return 0;
-
- assert(type_lookup_member(link_options_type, "export_memory_name", &smem));
- opts->export_memory_name = get_expression_string_value(input->args.values[smem.idx], &out_is_valid);
- if (!out_is_valid) return 0;
-
- assert(type_lookup_member(link_options_type, "export_func_table", &smem));
- opts->export_func_table = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid) != 0;
- if (!out_is_valid) return 0;
-
- assert(type_lookup_member(link_options_type, "export_func_table_name", &smem));
- opts->export_func_table_name = get_expression_string_value(input->args.values[smem.idx], &out_is_valid);
- if (!out_is_valid) return 0;
-
- assert(type_lookup_member(link_options_type, "memory_min_size", &smem));
- opts->memory_min_size = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
- if (!out_is_valid) return 0;
-
- assert(type_lookup_member(link_options_type, "memory_max_size", &smem));
- opts->memory_max_size = get_expression_integer_value(input->args.values[smem.idx], &out_is_valid);
- if (!out_is_valid) return 0;
-
- return 1;
-}
-
-#include "wasm_output.h"
+++ /dev/null
-// This file is directly included in src/onxywasm.c
-// It is here purely to decrease the amount of clutter in the main file.
-
-
-// IMPROVE: This implementation assumes that the source and destination buffers do not overlap.
-// The specification for memory.copy in WASM does work even if the buffers overlap.
-// Also, this implementation copies byte-by-byte, which is terrible. It should copy
-// quad word by quad word, and then the additional bytes if the count was not divisible by 8.
-// :32BitPointers
-EMIT_FUNC_NO_ARGS(intrinsic_memory_copy) {
- bh_arr(WasmInstruction) code = *pcode;
-
- // The stack should look like this:
- // <count>
- // <source>
- // <dest>
-
- u64 count_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
- u64 source_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
- u64 dest_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-
- WIL(NULL, WI_LOCAL_SET, count_local);
- WIL(NULL, WI_LOCAL_SET, source_local);
- WIL(NULL, WI_LOCAL_SET, dest_local);
-
- // count is greater than 0
- WIL(NULL, WI_LOCAL_GET, count_local);
- WID(NULL, WI_I32_CONST, 0);
- WI(NULL, WI_I32_GT_S);
-
- WID(NULL, WI_IF_START, 0x40);
- WID(NULL, WI_LOOP_START, 0x40);
-
- WIL(NULL, WI_LOCAL_GET, count_local);
- WID(NULL, WI_I32_CONST, 1);
- WI(NULL, WI_I32_SUB);
- WIL(NULL, WI_LOCAL_SET, count_local);
-
- WIL(NULL, WI_LOCAL_GET, dest_local);
- WIL(NULL, WI_LOCAL_GET, count_local);
- WI(NULL, WI_PTR_ADD);
-
- WIL(NULL, WI_LOCAL_GET, source_local);
- WIL(NULL, WI_LOCAL_GET, count_local);
- WI(NULL, WI_PTR_ADD);
-
- WID(NULL, WI_I32_LOAD_8_U, ((WasmInstructionData) { 0, 0 }));
- WID(NULL, WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
-
- WIL(NULL, WI_LOCAL_GET, count_local);
- WID(NULL, WI_I32_CONST, 0);
- WI(NULL, WI_I32_GT_S);
- WID(NULL, WI_COND_JUMP, 0x00);
-
- WI(NULL, WI_LOOP_END);
- WI(NULL, WI_IF_END);
-
- local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-
- *pcode = code;
-}
-
-EMIT_FUNC_NO_ARGS(intrinsic_memory_fill) {
- bh_arr(WasmInstruction) code = *pcode;
-
- // The stack should look like this:
- // <count>
- // <byte>
- // <dest>
-
- u64 count_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
- u64 byte_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
- u64 dest_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-
- WIL(NULL, WI_LOCAL_SET, count_local);
- WIL(NULL, WI_LOCAL_SET, byte_local);
- WIL(NULL, WI_LOCAL_SET, dest_local);
-
- // count is greater than 0
- WIL(NULL, WI_LOCAL_GET, count_local);
- WID(NULL, WI_I32_CONST, 0);
- WI(NULL, WI_I32_GT_S);
-
- WID(NULL, WI_IF_START, 0x40);
- WID(NULL, WI_LOOP_START, 0x40);
-
- WIL(NULL, WI_LOCAL_GET, count_local);
- WID(NULL, WI_I32_CONST, 1);
- WI(NULL, WI_I32_SUB);
- WIL(NULL, WI_LOCAL_SET, count_local);
-
- WIL(NULL, WI_LOCAL_GET, dest_local);
- WIL(NULL, WI_LOCAL_GET, count_local);
- WI(NULL, WI_PTR_ADD);
-
- WIL(NULL, WI_LOCAL_GET, byte_local);
- WID(NULL, WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
-
- WIL(NULL, WI_LOCAL_GET, count_local);
- WID(NULL, WI_I32_CONST, 0);
- WI(NULL, WI_I32_GT_S);
- WID(NULL, WI_COND_JUMP, 0x00);
-
- WI(NULL, WI_LOOP_END);
- WI(NULL, 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(NULL, 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(NULL, 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, Error_Critical,
- "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;
-}
-
-EMIT_FUNC(intrinsic_atomic_wait, Type* type, OnyxToken* where) {
- if (type->kind != Type_Kind_Basic) goto bad_type;
-
- bh_arr(WasmInstruction) code = *pcode;
-
- switch (type->Basic.kind) {
- case Basic_Kind_I32:
- case Basic_Kind_U32: WID(NULL, WI_ATOMIC_WAIT32, ((WasmInstructionData) { 2, 0 })); break;
-
- case Basic_Kind_I64:
- case Basic_Kind_U64: WID(NULL, WI_ATOMIC_WAIT64, ((WasmInstructionData) { 3, 0 })); break;
-
- default: goto bad_type;
- }
-
- *pcode = code;
- return;
-
-bad_type:
- onyx_report_error(where->pos, Error_Critical, "Bad type for atomic wait, '%s'. Only i32 and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC_NO_ARGS(intrinsic_atomic_notify) {
- bh_arr(WasmInstruction) code = *pcode;
- WID(NULL, WI_ATOMIC_NOTIFY, ((WasmInstructionData) { 2, 0 }));
- *pcode = code;
-}
-
-EMIT_FUNC_NO_ARGS(intrinsic_atomic_fence) {
- bh_arr(WasmInstruction) code = *pcode;
- WI(NULL, WI_ATOMIC_FENCE);
- *pcode = code;
-}
-
-EMIT_FUNC(intrinsic_atomic_load, Type* type, OnyxToken* where) {
- if (type->kind != Type_Kind_Basic) goto bad_type;
-
- bh_arr(WasmInstruction) code = *pcode;
-
- switch (type->Basic.kind) {
- case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_LOAD8_U, ((WasmInstructionData) { 0, 0 })); break;
- case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_LOAD16_U, ((WasmInstructionData) { 1, 0 })); break;
-
- case Basic_Kind_I32:
- case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_LOAD, ((WasmInstructionData) { 2, 0 })); break;
-
- case Basic_Kind_I64:
- case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_LOAD, ((WasmInstructionData) { 3, 0 })); break;
-
- default: goto bad_type;
- }
-
- *pcode = code;
- return;
-
-bad_type:
- onyx_report_error(where->pos, Error_Critical, "Bad type for atomic load, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC(intrinsic_atomic_store, Type* type, OnyxToken* where) {
- if (type->kind != Type_Kind_Basic) goto bad_type;
-
- bh_arr(WasmInstruction) code = *pcode;
-
- switch (type->Basic.kind) {
- case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_STORE8, ((WasmInstructionData) { 0, 0 })); break;
- case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_STORE16, ((WasmInstructionData) { 1, 0 })); break;
-
- case Basic_Kind_I32:
- case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_STORE, ((WasmInstructionData) { 2, 0 })); break;
-
- case Basic_Kind_I64:
- case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_STORE, ((WasmInstructionData) { 3, 0 })); break;
-
- default: goto bad_type;
- }
-
- *pcode = code;
- return;
-
-bad_type:
- onyx_report_error(where->pos, Error_Critical, "Bad type for atomic store, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC(intrinsic_atomic_add, Type* type, OnyxToken* where) {
- if (type->kind != Type_Kind_Basic) goto bad_type;
-
- bh_arr(WasmInstruction) code = *pcode;
-
- switch (type->Basic.kind) {
- case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_ADD8_U, ((WasmInstructionData) { 0, 0 })); break;
- case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_ADD16_U, ((WasmInstructionData) { 1, 0 })); break;
-
- case Basic_Kind_I32:
- case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_ADD, ((WasmInstructionData) { 2, 0 })); break;
-
- case Basic_Kind_I64:
- case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_ADD, ((WasmInstructionData) { 3, 0 })); break;
-
- default: goto bad_type;
- }
-
- *pcode = code;
- return;
-
-bad_type:
- onyx_report_error(where->pos, Error_Critical, "Bad type for atomic add, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC(intrinsic_atomic_sub, Type* type, OnyxToken* where) {
- if (type->kind != Type_Kind_Basic) goto bad_type;
-
- bh_arr(WasmInstruction) code = *pcode;
-
- switch (type->Basic.kind) {
- case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_SUB8_U, ((WasmInstructionData) { 0, 0 })); break;
- case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_SUB16_U, ((WasmInstructionData) { 1, 0 })); break;
-
- case Basic_Kind_I32:
- case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_SUB, ((WasmInstructionData) { 2, 0 })); break;
-
- case Basic_Kind_I64:
- case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_SUB, ((WasmInstructionData) { 3, 0 })); break;
-
- default: goto bad_type;
- }
-
- *pcode = code;
- return;
-
-bad_type:
- onyx_report_error(where->pos, Error_Critical, "Bad type for atomic sub, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC(intrinsic_atomic_and, Type* type, OnyxToken* where) {
- if (type->kind != Type_Kind_Basic) goto bad_type;
-
- bh_arr(WasmInstruction) code = *pcode;
-
- switch (type->Basic.kind) {
- case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_AND8_U, ((WasmInstructionData) { 0, 0 })); break;
- case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_AND16_U, ((WasmInstructionData) { 1, 0 })); break;
-
- case Basic_Kind_I32:
- case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_AND, ((WasmInstructionData) { 2, 0 })); break;
-
- case Basic_Kind_I64:
- case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_AND, ((WasmInstructionData) { 3, 0 })); break;
-
- default: goto bad_type;
- }
-
- *pcode = code;
- return;
-
-bad_type:
- onyx_report_error(where->pos, Error_Critical, "Bad type for atomic and, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC(intrinsic_atomic_or, Type* type, OnyxToken* where) {
- if (type->kind != Type_Kind_Basic) goto bad_type;
-
- bh_arr(WasmInstruction) code = *pcode;
-
- switch (type->Basic.kind) {
- case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_OR8_U, ((WasmInstructionData) { 0, 0 })); break;
- case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_OR16_U, ((WasmInstructionData) { 1, 0 })); break;
-
- case Basic_Kind_I32:
- case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_OR, ((WasmInstructionData) { 2, 0 })); break;
-
- case Basic_Kind_I64:
- case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_OR, ((WasmInstructionData) { 3, 0 })); break;
-
- default: goto bad_type;
- }
-
- *pcode = code;
- return;
-
-bad_type:
- onyx_report_error(where->pos, Error_Critical, "Bad type for atomic or, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC(intrinsic_atomic_xor, Type* type, OnyxToken* where) {
- if (type->kind != Type_Kind_Basic) goto bad_type;
-
- bh_arr(WasmInstruction) code = *pcode;
-
- switch (type->Basic.kind) {
- case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_XOR8_U, ((WasmInstructionData) { 0, 0 })); break;
- case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_XOR16_U, ((WasmInstructionData) { 1, 0 })); break;
-
- case Basic_Kind_I32:
- case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_XOR, ((WasmInstructionData) { 2, 0 })); break;
-
- case Basic_Kind_I64:
- case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_XOR, ((WasmInstructionData) { 3, 0 })); break;
-
- default: goto bad_type;
- }
-
- *pcode = code;
- return;
-
-bad_type:
- onyx_report_error(where->pos, Error_Critical, "Bad type for atomic xor, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC(intrinsic_atomic_xchg, Type* type, OnyxToken* where) {
- if (type->kind != Type_Kind_Basic) goto bad_type;
-
- bh_arr(WasmInstruction) code = *pcode;
-
- switch (type->Basic.kind) {
- case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_XCHG8_U, ((WasmInstructionData) { 0, 0 })); break;
- case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_XCHG16_U, ((WasmInstructionData) { 1, 0 })); break;
-
- case Basic_Kind_I32:
- case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_XCHG, ((WasmInstructionData) { 2, 0 })); break;
-
- case Basic_Kind_I64:
- case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_XCHG, ((WasmInstructionData) { 3, 0 })); break;
-
- default: goto bad_type;
- }
-
- *pcode = code;
- return;
-
-bad_type:
- onyx_report_error(where->pos, Error_Critical, "Bad type for atomic xchg, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC(intrinsic_atomic_cmpxchg, Type* type, OnyxToken* where) {
- if (type->kind != Type_Kind_Basic) goto bad_type;
-
- bh_arr(WasmInstruction) code = *pcode;
-
- switch (type->Basic.kind) {
- case Basic_Kind_U8: WID(NULL, WI_ATOMIC_I32_CMPXCHG8_U, ((WasmInstructionData) { 0, 0 })); break;
- case Basic_Kind_U16: WID(NULL, WI_ATOMIC_I32_CMPXCHG16_U, ((WasmInstructionData) { 1, 0 })); break;
-
- case Basic_Kind_I32:
- case Basic_Kind_U32: WID(NULL, WI_ATOMIC_I32_CMPXCHG, ((WasmInstructionData) { 2, 0 })); break;
-
- case Basic_Kind_I64:
- case Basic_Kind_U64: WID(NULL, WI_ATOMIC_I64_CMPXCHG, ((WasmInstructionData) { 3, 0 })); break;
-
- default: goto bad_type;
- }
-
- *pcode = code;
- return;
-
-bad_type:
- onyx_report_error(where->pos, Error_Critical, "Bad type for atomic cmpxchg, '%s'. Only u8, u16, u32, i32, u64, and i64 are supported.", type_get_name(type));
-}
-
-EMIT_FUNC_NO_ARGS(initialize_data_segments_body) {
- // :ProperLinking
- if (!context.options->use_multi_threading || !context.options->use_post_mvp_features) return;
-
- bh_arr(WasmInstruction) code = *pcode;
-
- //
- // Because this code is generated direction in the function
- // it is assumed that EVERY data entry will be entered by
- // this point. If data section entries can be entered after
- // function body generation starts, this code will have to
- // move to a link phase thing.
- i32 index = 0;
- bh_arr_each(WasmDatum, datum, mod->data) {
- assert(datum->id > 0);
- if (datum->data == NULL) { index++; continue; }
-
- emit_data_relocation(mod, &code, datum->id);
- WID(NULL, WI_PTR_CONST, 0);
- WID(NULL, WI_I32_CONST, datum->length);
- WID(NULL, WI_MEMORY_INIT, ((WasmInstructionData) { index, 0 }));
-
- index += 1;
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC_NO_ARGS(run_init_procedures) {
- bh_arr(WasmInstruction) code = *pcode;
-
- bh_arr_each(AstFunction *, func, init_procedures) {
- i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) *func);
- debug_emit_instruction(mod, NULL);
- bh_arr_push(code, ((WasmInstruction){ WI_CALL, func_idx }));
- }
-
- *pcode = code;
-}
+++ /dev/null
-// This file is included in src/onyxwasm.c.
-// It is separated because of its fundamentally different goals.
-
-//-------------------------------------------------
-// BINARY OUPUT
-//-------------------------------------------------
-
-#define WASM_SECTION_ID_CUSTOM 0
-#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_DATACOUNT 12
-#define WASM_SECTION_ID_CODE 10
-#define WASM_SECTION_ID_DATA 11
-
-typedef i32 vector_func(void*, bh_buffer*);
-
-static const u8 ONYX_MAGIC_STRING[] = "ONYX";
-static const u8 WASM_MAGIC_STRING[] = { 0x00, 0x61, 0x73, 0x6D };
-static const u8 WASM_VERSION[] = { 0x01, 0x00, 0x00, 0x00 };
-
-static inline i32 output_unsigned_integer(u64 i, bh_buffer *buff) {
- i32 leb_len;
- u8* leb = uint_to_uleb128(i, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- return leb_len;
-}
-
-static inline i32 output_custom_section_name(char *name, bh_buffer *buff) {
- u32 len = strlen(name);
- i32 len_len = output_unsigned_integer(len, buff);
- bh_buffer_append(buff, name, len);
- return len_len + len;
-}
-
-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, b32 shared, bh_buffer* buff) {
- i32 leb_len, prev_len = buff->length;
- u8* leb;
-
- u8 mem_type = 0x00;
- if (max >= 0) mem_type |= 0x01;
- if (shared) mem_type |= 0x02;
-
- bh_buffer_write_byte(buff, mem_type);
-
- 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, 0, &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) {
- // :ProperLinking
- // if (context.options->use_multi_threading) return 0;
- if (!module->needs_memory_section) return 0;
-
- 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);
-
- output_limits(module->memory_min_size, -1, 0, &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, strlen(import->mod), &vec_buff);
- output_name(import->name, strlen(import->name), &vec_buff);
- bh_buffer_write_byte(&vec_buff, (u8) import->kind);
-
- switch (import->kind) {
- case WASM_FOREIGN_FUNCTION:
- leb = uint_to_uleb128((u64) import->idx, &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
- break;
-
- case WASM_FOREIGN_MEMORY:
- output_limits(import->min, import->max, import->shared, &vec_buff);
- break;
-
- case WASM_FOREIGN_TABLE: assert(0);
- }
- }
-
- 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;
- fori (i, 0, shlen(module->exports)) {
- char *key = module->exports[i].key;
- WasmExport value = module->exports[i].value;
-
- 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);
- }
-
- 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;
- fori (i, 0, shlen(module->exports)) {
- char *key = module->exports[i].key;
- WasmExport value = module->exports[i].value;
-
- if (value.kind == WASM_FOREIGN_FUNCTION) {
- if (strncmp("main", key, 5) == 0) {
- start_idx = value.idx;
- break;
- }
- }
- }
-
- 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_UNREACHABLE) assert(("EMITTING UNREACHABLE!!", 0));
-
- if (instr->type == WI_NOP && !context.options->debug_enabled) 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 if (instr->type & ATOMIC_INSTR_MASK) {
- bh_buffer_write_byte(buff, 0xFE);
- leb = uint_to_uleb128((u64) (instr->type &~ ATOMIC_INSTR_MASK), &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- if (instr->type == WI_ATOMIC_FENCE) {
- bh_buffer_write_byte(buff, 0x00);
-
- } else {
- 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);
- }
-
- } 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_INIT:
- 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);
-
- assert(func->code);
-
- // 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);
-
- 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_datacountsection(OnyxWasmModule* module, bh_buffer* buff) {
- if (!context.options->use_post_mvp_features) return 0;
-
- i32 prev_len = buff->length;
-
- bh_buffer_write_byte(buff, WASM_SECTION_ID_DATACOUNT);
-
- 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);
-
- 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) {
- i32 memory_flags = 0x00;
- // :ProperLinking
- if (context.options->use_multi_threading) memory_flags |= 0x01;
-
- bh_buffer_write_byte(&vec_buff, memory_flags);
-
- // :ProperLinking
- if (!context.options->use_multi_threading) {
- 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);
- }
-
- if (datum->data != NULL) {
- 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]);
- } else {
- leb = uint_to_uleb128(0, &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_onyx_libraries_section(OnyxWasmModule* module, bh_buffer* buff) {
- if (bh_arr_length(module->libraries) == 0) return 0;
- i32 prev_len = buff->length;
-
- bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
-
- bh_buffer libs_buff;
- bh_buffer_init(&libs_buff, buff->allocator, 128);
-
- output_custom_section_name("_onyx_libs", &libs_buff);
-
- output_unsigned_integer(bh_arr_length(module->library_paths), &libs_buff);
-
- bh_arr_each(char *, lib, module->library_paths) {
- assert(*lib != NULL);
-
- u32 lib_len = strlen(*lib);
- output_unsigned_integer(lib_len, &libs_buff);
- bh_buffer_append(&libs_buff, *lib, lib_len);
- }
-
- output_unsigned_integer(bh_arr_length(module->libraries), &libs_buff);
-
- bh_arr_each(char *, lib, module->libraries) {
- assert(*lib != NULL);
-
- u32 lib_len = strlen(*lib);
- output_unsigned_integer(lib_len, &libs_buff);
- bh_buffer_append(&libs_buff, *lib, lib_len);
- }
-
- output_unsigned_integer(libs_buff.length, buff);
-
- bh_buffer_concat(buff, libs_buff);
- bh_buffer_free(&libs_buff);
-
- return buff->length - prev_len;
-}
-
-static i32 output_onyx_func_offset_section(OnyxWasmModule* module, bh_buffer* buff) {
- i32 prev_len = buff->length;
-
- bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
-
- bh_buffer section_buff;
- bh_buffer_init(§ion_buff, buff->allocator, 128);
-
- output_custom_section_name("_onyx_func_offsets", §ion_buff);
-
- i32 func_count = bh_arr_length(module->funcs) + module->foreign_function_count;
-
- bh_buffer name_buff;
- bh_buffer_init(&name_buff, buff->allocator, 1024);
- u32 str_cursor = func_count * 4;
- fori (i, 0, func_count) {
- bh_buffer_write_u32(§ion_buff, str_cursor);
-
- if (i < module->foreign_function_count) {
- bh_buffer_append(&name_buff, "<imported function>", 20);
- str_cursor += 20;
- } else {
- WasmFunc *func = &module->funcs[i - module->foreign_function_count];
- assert(func->location);
- char *str = bh_bprintf("%s:%d,%d\0", func->location->pos.filename, func->location->pos.line, func->location->pos.column);
- i32 len = strlen(str);
- bh_buffer_append(&name_buff, str, len + 1);
- str_cursor += len + 1;
- }
- }
-
- bh_buffer_concat(§ion_buff, name_buff);
-
- output_unsigned_integer(section_buff.length, buff);
-
- bh_buffer_concat(buff, section_buff);
- bh_buffer_free(§ion_buff);
-
- return buff->length - prev_len;
-}
-
-#ifdef ENABLE_DEBUG_INFO
-static i32 output_ovm_debug_sections(OnyxWasmModule* module, bh_buffer* buff) {
- if (!module->debug_context || !context.options->debug_enabled) return 0;
-
- DebugContext *ctx = module->debug_context;
-
- bh_buffer section_buff;
- bh_buffer_init(§ion_buff, buff->allocator, 128);
-
- {
- // ovm_debug_files section
- bh_buffer_clear(§ion_buff);
- bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
-
- output_custom_section_name("ovm_debug_files", §ion_buff);
-
- i32 file_count = shlenu(ctx->file_info);
- output_unsigned_integer(file_count, §ion_buff);
-
- fori (i, 0, file_count) {
- Table(DebugFileInfo) entry = (void *) &ctx->file_info[i];
- output_unsigned_integer(entry->value.file_id, §ion_buff);
- output_unsigned_integer(entry->value.line_count, §ion_buff);
- output_name(entry->key, strlen(entry->key), §ion_buff);
- }
-
- output_unsigned_integer(section_buff.length, buff);
-
- bh_buffer_concat(buff, section_buff);
- }
-
- {
- // ovm_debug_funcs section
- bh_buffer_clear(§ion_buff);
- bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
-
- output_custom_section_name("ovm_debug_funcs", §ion_buff);
-
- i32 func_count = bh_arr_length(ctx->funcs);
- output_unsigned_integer(func_count, §ion_buff);
-
- fori (i, 0, func_count) {
- DebugFuncContext *func = &ctx->funcs[i];
- output_unsigned_integer(func->func_index, §ion_buff);
- output_unsigned_integer(func->file_id, §ion_buff);
- output_unsigned_integer(func->line, §ion_buff);
- output_name(func->name, func->name_length, §ion_buff);
- output_unsigned_integer(1, §ion_buff);
- output_unsigned_integer(func->op_offset, §ion_buff);
-
- LocalAllocator *locals = &module->funcs[i].locals;
- if (func->stack_ptr_idx > 0) {
- u32 local_idx = local_lookup_idx(locals, func->stack_ptr_idx);
- output_unsigned_integer(local_idx, §ion_buff);
- } else {
- output_unsigned_integer(0, §ion_buff);
- }
-
- output_unsigned_integer(0, §ion_buff);
- }
-
- output_unsigned_integer(section_buff.length, buff);
-
- bh_buffer_concat(buff, section_buff);
- }
-
- {
- // ovm_debug_syms section
-
- // First, apply patches for register locations
- bh_arr_each(DebugSymPatch, patch, ctx->sym_patches) {
- // CLEANUP: This is (kind of) incorrect, as there is nothing guarenteeing
- // that the symbol with id a will be a position a, other than the way
- // that this has been implemented right now.
- assert(ctx->sym_info[patch->sym_id].location_type == DSL_REGISTER);
-
- LocalAllocator *locals = &module->funcs[patch->func_idx - module->foreign_function_count].locals;
- ctx->sym_info[patch->sym_id].location_num = local_lookup_idx(locals, patch->local_idx);
- }
-
- bh_buffer_clear(§ion_buff);
- bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
-
- output_custom_section_name("ovm_debug_syms", §ion_buff);
-
- i32 sym_count = bh_arr_length(ctx->sym_info);
- output_unsigned_integer(sym_count, §ion_buff);
-
- fori (i, 0, sym_count) {
- DebugSymInfo *sym = &ctx->sym_info[i];
- output_unsigned_integer(sym->sym_id, §ion_buff);
- if (sym->name) {
- output_name(sym->name, strlen(sym->name), §ion_buff);
- } else {
- output_unsigned_integer(0, §ion_buff);
- }
- output_unsigned_integer(sym->location_type, §ion_buff);
- output_unsigned_integer(sym->location_num, §ion_buff);
- output_unsigned_integer(sym->type, §ion_buff);
- }
-
- output_unsigned_integer(section_buff.length, buff);
-
- bh_buffer_concat(buff, section_buff);
- }
-
- {
- // ovm_debug_types section
- bh_buffer_clear(§ion_buff);
- bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
-
- output_custom_section_name("ovm_debug_types", §ion_buff);
-
- i32 type_count = bh_arr_length(type_map.entries);
- output_unsigned_integer(type_count, §ion_buff);
-
- bh_arr_each(bh__imap_entry, entry, type_map.entries) {
- u32 id = entry->key;
- Type *type = (Type *) entry->value;
- const char *name = type_get_name(type);
-
- output_unsigned_integer(id, §ion_buff);
- output_name(name, strlen(name), §ion_buff);
- output_unsigned_integer(type_size_of(type), §ion_buff);
-
- if (type->kind == Type_Kind_Basic) {
- // Type indicies are special because they are encoded
- // as a "distinct" unsigned 32-bit integer, which is
- // effectively how they are used in the code anyway.
- //
- // This is probably a change that will be made throughout
- // the entire compiler, but for now they will remain as
- // a special type.
- if (type->Basic.kind == Basic_Kind_Type_Index) {
- output_unsigned_integer(5, §ion_buff);
- output_unsigned_integer(2, §ion_buff);
- output_unsigned_integer(basic_types[Basic_Kind_U32].id, §ion_buff);
- continue;
- }
-
- if (type->Basic.kind == Basic_Kind_Rawptr) {
- // rawptr -> ^void
- output_unsigned_integer(2, §ion_buff);
- output_unsigned_integer(1, §ion_buff);
- output_unsigned_integer(basic_types[Basic_Kind_Void].id, §ion_buff);
- continue;
- }
-
- output_unsigned_integer(1, §ion_buff);
- if (type->Basic.kind == Basic_Kind_Void) output_unsigned_integer(0, §ion_buff);
- else if (type_is_bool(type)) output_unsigned_integer(4, §ion_buff);
- else if (type_is_integer(type)) {
- if (type->Basic.flags & Basic_Flag_Unsigned) output_unsigned_integer(2, §ion_buff);
- else output_unsigned_integer(1, §ion_buff);
- }
- else if (type->Basic.flags & Basic_Flag_Float) output_unsigned_integer(3, §ion_buff);
- else if (type_is_simd(type)) output_unsigned_integer(6, §ion_buff);
- else {
- output_unsigned_integer(0, §ion_buff);
- }
-
- continue;
- }
-
- if (type->kind == Type_Kind_Pointer) {
- output_unsigned_integer(2, §ion_buff);
- output_unsigned_integer(1, §ion_buff);
- output_unsigned_integer(type->Pointer.elem->id, §ion_buff);
- continue;
- }
-
- if (type->kind == Type_Kind_Enum) {
- output_unsigned_integer(5, §ion_buff);
- output_unsigned_integer(2, §ion_buff);
- output_unsigned_integer(type->Enum.backing->id, §ion_buff);
- continue;
- }
-
- if (type->kind == Type_Kind_Array) {
- output_unsigned_integer(4, §ion_buff);
- output_unsigned_integer(type->Array.count, §ion_buff);
- output_unsigned_integer(type->Array.elem->id, §ion_buff);
- continue;
- }
-
- if (type_is_structlike_strict(type)) {
- output_unsigned_integer(3, §ion_buff);
-
- i32 mem_count = type_structlike_mem_count(type);
- output_unsigned_integer(mem_count, §ion_buff);
-
- fori (i, 0, mem_count) {
- StructMember smem;
- type_lookup_member_by_idx(type, i, &smem);
-
- output_unsigned_integer(smem.offset, §ion_buff);
- output_unsigned_integer(smem.type->id, §ion_buff);
- output_name(smem.name, strlen(smem.name), §ion_buff);
- }
-
- continue;
- }
-
- if (type->kind == Type_Kind_Function) {
- output_unsigned_integer(6, §ion_buff);
- output_unsigned_integer(type->Function.param_count, §ion_buff);
-
- fori (i, 0, (i32) type->Function.param_count) {
- output_unsigned_integer(type->Function.params[i]->id, §ion_buff);
- }
-
- output_unsigned_integer(type->Function.return_type->id, §ion_buff);
- continue;
- }
-
- if (type->kind == Type_Kind_Distinct) {
- output_unsigned_integer(5, §ion_buff);
- output_unsigned_integer(2, §ion_buff);
- output_unsigned_integer(type->Distinct.base_type->id, §ion_buff);
- continue;
- }
-
- // No debug information will be given about the poly struct
- // or compound types.
- // Outside of runtime type information, they provide no useful
- // debugging information (I don't think at least...).
- if (type->kind == Type_Kind_PolyStruct ||
- type->kind == Type_Kind_Compound) {
- output_unsigned_integer(1, §ion_buff);
- output_unsigned_integer(0, §ion_buff);
- continue;
- }
-
- assert(("Unhandled type", 0));
- }
-
- output_unsigned_integer(section_buff.length, buff);
-
- bh_buffer_concat(buff, section_buff);
- }
-
- {
- // ovm_debug_ops section
- bh_buffer_clear(§ion_buff);
- bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
-
- output_custom_section_name("ovm_debug_ops", §ion_buff);
- bh_buffer_concat(§ion_buff, ctx->op_buffer);
-
- output_unsigned_integer(section_buff.length, buff);
-
- bh_buffer_concat(buff, section_buff);
- }
-
- bh_buffer_free(§ion_buff);
- return 0;
-}
-#endif
-
-void onyx_wasm_module_write_to_buffer(OnyxWasmModule* module, bh_buffer* buffer) {
- bh_buffer_init(buffer, global_heap_allocator, 128);
- if (context.options->runtime == Runtime_Onyx) {
- bh_buffer_append(buffer, ONYX_MAGIC_STRING, 4);
- } else {
- bh_buffer_append(buffer, WASM_MAGIC_STRING, 4);
- }
- bh_buffer_append(buffer, WASM_VERSION, 4);
-
-#ifdef ENABLE_DEBUG_INFO
- output_ovm_debug_sections(module, buffer);
-#endif
- output_typesection(module, buffer);
- output_importsection(module, buffer);
- output_funcsection(module, buffer);
- output_tablesection(module, buffer);
- output_memorysection(module, buffer);
- output_globalsection(module, buffer);
- output_exportsection(module, buffer);
- output_startsection(module, buffer);
- output_elemsection(module, buffer);
- output_datacountsection(module, buffer);
- output_codesection(module, buffer);
- output_datasection(module, buffer);
- output_onyx_libraries_section(module, buffer);
-
- // TODO: Consider if this should always be included?
- // It can amount to a lot of extra data.
- output_onyx_func_offset_section(module, buffer);
-}
-
-void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file) {
- bh_buffer master_buffer;
- onyx_wasm_module_write_to_buffer(module, &master_buffer);
-
- bh_file_write(&file, master_buffer.data, master_buffer.length);
-}
+++ /dev/null
-#include "bh.h"
-#include "utils.h"
-#include "astnodes.h"
-#include "wasm.h"
-#include "onyx_library.h"
-
-#ifndef USE_OVM_DEBUGGER
- #include "wasmer.h"
-#endif
-
-#ifdef _BH_LINUX
- #include <pthread.h>
- #include <signal.h>
- #include <sys/wait.h>
- #include <dlfcn.h>
-#endif
-
-static wasm_config_t* wasm_config;
-static wasm_engine_t* wasm_engine;
-static wasm_store_t* wasm_store;
-static wasm_extern_vec_t wasm_imports;
-static bh_buffer wasm_raw_bytes;
-wasm_instance_t* wasm_instance;
-wasm_module_t* wasm_module;
-wasm_memory_t* wasm_memory;
-
-OnyxRuntime wasm_runtime;
-
-b32 wasm_name_equals(const wasm_name_t* name1, const wasm_name_t* name2) {
- if (name1->size != name2->size) return 0;
- return !strncmp(name1->data, name2->data, name1->size);
-}
-
-b32 wasm_name_equals_string(const wasm_name_t* name1, const char* name2) {
- u32 name2_size = strlen(name2);
- if (name1->size != name2_size) return 0;
- return !strncmp(name1->data, name2, name1->size);
-}
-
-wasm_extern_t* wasm_extern_lookup_by_name(wasm_module_t* module, wasm_instance_t* instance, const char* name) {
- i32 name_len = strlen(name);
-
- i32 idx = -1;
- wasm_exporttype_vec_t export_types;
- wasm_module_exports(module, &export_types);
- fori (i, 0, (i64) export_types.size) {
- wasm_exporttype_t* export_type = export_types.data[i];
- const wasm_name_t* export_name = wasm_exporttype_name(export_type);
-
- if (!strncmp(export_name->data, name, name_len)) {
- idx = i;
- break;
- }
- }
-
- if (idx == -1) return NULL;
-
- wasm_extern_vec_t exports;
- wasm_instance_exports(instance, &exports);
-
- return exports.data[idx];
-}
-
-
-
-typedef void *(*LibraryLinker)(OnyxRuntime *runtime);
-
-typedef struct LibraryLinkContext {
- bh_buffer wasm_bytes;
- bh_arr(char *) library_paths;
-} LibraryLinkContext;
-
-static WasmFuncDefinition** onyx_load_library(LibraryLinkContext *ctx, char *name) {
- #ifdef _BH_LINUX
- #define DIR_SEPARATOR '/'
- #endif
- #ifdef _BH_WINDOWS
- #define DIR_SEPARATOR '\\'
- #endif
-
- char *library = name;
- fori (i, 0, (i32) strlen(name)) {
- if (name[i] == DIR_SEPARATOR) library = &name[i + 1];
- }
-
- char *library_load_name_tmp = bh_bprintf("onyx_library_%s", library);
- char *library_load_name = alloca(strlen(library_load_name_tmp) + 1);
- strcpy(library_load_name, library_load_name_tmp);
- LibraryLinker library_load;
-
- #ifdef _BH_LINUX
- char *library_name = bh_lookup_file(name, ".", ".so", 1, (const char **) ctx->library_paths, 1);
- void* handle = dlopen(library_name, RTLD_LAZY);
- if (handle == NULL) {
- printf("ERROR LOADING LIBRARY %s: %s\n", name, dlerror());
- return NULL;
- }
-
- library_load = (LibraryLinker) dlsym(handle, library_load_name);
- if (library_load == NULL) {
- printf("ERROR RESOLVING '%s': %s\n", library_load_name, dlerror());
- return NULL;
- }
- #endif
-
- #ifdef _BH_WINDOWS
- char *library_name = bh_lookup_file(name, ".", ".dll", 1, (const char **) ctx->library_paths, 1);
- HMODULE handle = LoadLibraryA(library_name);
- if (handle == NULL) {
- printf("ERROR LOADING LIBRARY %s: %d\n", name, GetLastError());
- return NULL;
- }
-
- library_load = (LibraryLinker) GetProcAddress(handle, library_load_name);
- if (library_load == NULL) {
- printf("ERROR RESOLVING '%s': %d\n", library_load_name, GetLastError());
- return NULL;
- }
- #endif
-
- return library_load(runtime);
-}
-
-static void lookup_and_load_custom_libraries(LibraryLinkContext *ctx, bh_arr(WasmFuncDefinition **)* p_out) {
- bh_arr(WasmFuncDefinition **) out = *p_out;
-
- bh_buffer wasm_bytes = ctx->wasm_bytes;
-
- i32 cursor = 8; // skip the magic number and version
- while (cursor < wasm_bytes.length) {
- u64 section_number = uleb128_to_uint(wasm_bytes.data, &cursor);
- u64 section_size = uleb128_to_uint(wasm_bytes.data, &cursor);
-
- i32 section_start = cursor;
- if (section_number == 0) {
- u64 name_len = uleb128_to_uint(wasm_bytes.data, &cursor);
- if (!strncmp(wasm_bytes.data + cursor, "_onyx_libs", name_len)) {
- cursor += name_len;
- u64 lib_count = uleb128_to_uint(wasm_bytes.data, &cursor);
-
- fori (i, 0, (i64) lib_count) {
- u64 lib_path_length = uleb128_to_uint(wasm_bytes.data, &cursor);
- lib_path_length = bh_min(lib_path_length, 512);
-
- char *lib_path = malloc(lib_path_length);
- strncpy(lib_path, wasm_bytes.data + cursor, lib_path_length);
- lib_path[lib_path_length] = '\0';
- bh_path_convert_separators(lib_path);
- cursor += lib_path_length;
-
- bh_arr_push(ctx->library_paths, lib_path);
- }
-
- lib_count = uleb128_to_uint(wasm_bytes.data, &cursor);
-
- fori (i, 0, (i64) lib_count) {
- u64 lib_name_length = uleb128_to_uint(wasm_bytes.data, &cursor);
- lib_name_length = bh_min(lib_name_length, 256);
-
- char library_name[256];
- strncpy(library_name, wasm_bytes.data + cursor, lib_name_length);
- library_name[lib_name_length] = '\0';
- cursor += lib_name_length;
-
- WasmFuncDefinition** lib = onyx_load_library(ctx, library_name);
- if (lib) {
- bh_arr_push(out, lib);
- }
- }
- break;
- }
- }
-
- cursor = section_start + section_size;
- }
-
- *p_out = out;
-}
-
-static void onyx_print_trap(wasm_trap_t* trap) {
- wasm_message_t msg;
- wasm_trap_message(trap, &msg);
- bh_printf("TRAP: %b\n", msg.data, msg.size);
-
- i32 func_name_section = 0;
-
- i32 cursor = 8; // skip the magic number and version
- while (cursor < wasm_raw_bytes.length) {
- u64 section_number = uleb128_to_uint(wasm_raw_bytes.data, &cursor);
- u64 section_size = uleb128_to_uint(wasm_raw_bytes.data, &cursor);
-
- i32 section_start = cursor;
- if (section_number == 0) {
- u64 name_len = uleb128_to_uint(wasm_raw_bytes.data, &cursor);
- if (!strncmp(wasm_raw_bytes.data + cursor, "_onyx_func_offsets", name_len)) {
- cursor += name_len;
- func_name_section = cursor;
- break;
- }
- }
-
- cursor = section_start + section_size;
- }
-
- if (func_name_section == 0) return;
-
- bh_printf("TRACE:\n");
- wasm_frame_vec_t frames;
- wasm_trap_trace(trap, &frames);
- fori (i, 0, (i32) frames.size) {
- i32 func_idx = wasm_frame_func_index(frames.data[i]);
- i32 mod_offset = wasm_frame_module_offset(frames.data[i]);
-
- i32 cursor = func_name_section + 4 * func_idx;
- i32 func_offset = *(i32 *) (wasm_raw_bytes.data + cursor);
- char* func_name = wasm_raw_bytes.data + func_name_section + func_offset;
-
- bh_printf(" func[%d]:%p at %s\n", func_idx, mod_offset, func_name);
- }
-}
-
-static void cleanup_wasm_objects() {
- if (wasm_instance) wasm_instance_delete(wasm_instance);
- if (wasm_module) wasm_module_delete(wasm_module);
- if (wasm_store) wasm_store_delete(wasm_store);
- if (wasm_engine) wasm_engine_delete(wasm_engine);
-}
-
-//
-// This could be cleaned up a bit, as this function directly modifies various global variables.
-// Those being wasm_memory and wasm_imports.
-static b32 link_wasm_imports(bh_arr(WasmFuncDefinition **) linkable_functions, wasm_module_t *wasm_module) {
- wasm_importtype_vec_t module_imports;
- wasm_module_imports(wasm_module, &module_imports);
-
- wasm_extern_vec_new_uninitialized(&wasm_imports, module_imports.size); // @Free
-
- fori (i, 0, (i32) module_imports.size) {
- const wasm_name_t* module_name = wasm_importtype_module(module_imports.data[i]);
- const wasm_name_t* import_name = wasm_importtype_name(module_imports.data[i]);
-
- wasm_extern_t* import = NULL;
-
- if (wasm_name_equals_string(module_name, "onyx")) {
- if (wasm_name_equals_string(import_name, "memory")) {
- if (wasm_memory == NULL) {
- wasm_limits_t limits = { 1024, 65536 };
- wasm_memorytype_t* memory_type = wasm_memorytype_new(&limits);
- wasm_memory = wasm_memory_new(wasm_store, memory_type);
- }
-
- import = wasm_memory_as_extern(wasm_memory);
- goto import_found;
- }
- }
-
- bh_arr_each(WasmFuncDefinition **, library_funcs, linkable_functions) {
- WasmFuncDefinition **pcurrent_function = *library_funcs;
- while (*pcurrent_function != NULL) {
- WasmFuncDefinition *cf = *pcurrent_function;
- if (wasm_name_equals_string(module_name, cf->module_name) && wasm_name_equals_string(import_name, cf->import_name)) {
- wasm_valtype_vec_t wasm_params;
- wasm_valtype_vec_new_uninitialized(&wasm_params, cf->params->count);
- fori (k, 0, cf->params->count) wasm_params.data[k] = wasm_valtype_new(cf->params->types[k]);
-
- wasm_valtype_vec_t wasm_results;
- wasm_valtype_vec_new_uninitialized(&wasm_results, cf->results->count);
- fori (k, 0, cf->results->count) wasm_results.data[k] = wasm_valtype_new(cf->results->types[k]);
-
- wasm_functype_t* wasm_functype = wasm_functype_new(&wasm_params, &wasm_results);
-
- wasm_func_t* wasm_func = wasm_func_new(wasm_store, wasm_functype, cf->func);
- import = wasm_func_as_extern(wasm_func);
- goto import_found;
- }
-
- pcurrent_function += 1;
- }
- }
-
- goto bad_import;
-
- import_found:
- wasm_imports.data[i] = import;
- continue;
-
-
- bad_import:
- bh_printf("Couldn't find import %b.%b.\n", module_name->data, module_name->size, import_name->data, import_name->size);
- return 0;
- }
-
- return 1;
-}
-
-void onyx_run_initialize(b32 debug_enabled) {
- wasm_config = wasm_config_new();
- if (!wasm_config) {
- cleanup_wasm_objects();
- return;
- }
-
-#ifdef USE_OVM_DEBUGGER
- void wasm_config_enable_debug(wasm_config_t *config, int value);
- wasm_config_enable_debug(wasm_config, debug_enabled);
-#endif
-
-#ifndef USE_OVM_DEBUGGER
- if (debug_enabled) {
- printf("Warning: --debug does nothing if libovmwasm.so is not being used!\n");
- }
-
- // Prefer the LLVM compile because it is faster. This should be configurable from the command line and/or a top-level directive.
- if (wasmer_is_compiler_available(LLVM)) {
- wasm_config_set_compiler(wasm_config, LLVM);
- }
-
- wasmer_features_t* features = wasmer_features_new();
- wasmer_features_simd(features, 1);
- wasmer_features_threads(features, 1);
- wasmer_features_bulk_memory(features, 1);
- wasm_config_set_features(wasm_config, features);
-#endif
-
- wasm_engine = wasm_engine_new_with_config(wasm_config);
- if (!wasm_engine) {
- cleanup_wasm_objects();
- return;
- }
-
- wasm_store = wasm_store_new(wasm_engine);
- if (!wasm_store) {
- cleanup_wasm_objects();
- return;
- }
-
- // See comment in onyx_library.h about us being the linker.
- wasm_runtime.wasm_memory_data = &wasm_memory_data;
- wasm_runtime.wasm_extern_lookup_by_name = &wasm_extern_lookup_by_name;
- wasm_runtime.wasm_extern_as_func = &wasm_extern_as_func;
- wasm_runtime.wasm_func_call = &wasm_func_call;
- wasm_runtime.wasm_instance_new = &wasm_instance_new;
- wasm_runtime.wasm_store_new = &wasm_store_new;
- wasm_runtime.wasm_store_delete = &wasm_store_delete;
- wasm_runtime.onyx_print_trap = &onyx_print_trap;
-}
-
-b32 onyx_run_wasm(bh_buffer wasm_bytes, int argc, char *argv[]) {
- runtime = &wasm_runtime;
-
- if (strncmp(wasm_bytes.data, "ONYX", 4)) {
- printf("Bad magic bytes for Onyx binary.\n");
- return 0;
- }
-
- wasm_bytes.data[0] = '\0';
- wasm_bytes.data[1] = 'a';
- wasm_bytes.data[2] = 's';
- wasm_bytes.data[3] = 'm';
- wasm_raw_bytes = wasm_bytes;
-
- bh_arr(WasmFuncDefinition **) linkable_functions = NULL;
- bh_arr_new(bh_heap_allocator(), linkable_functions, 4);
-
- {
- LibraryLinkContext lib_ctx;
- lib_ctx.wasm_bytes = wasm_bytes;
- lib_ctx.library_paths = NULL;
- lookup_and_load_custom_libraries(&lib_ctx, &linkable_functions);
-
- bh_arr_free(lib_ctx.library_paths);
- }
-
- wasm_byte_vec_t wasm_data;
- wasm_data.size = wasm_bytes.length;
- wasm_data.data = wasm_bytes.data;
-
- wasm_module = wasm_module_new(wasm_store, &wasm_data);
- if (!wasm_module) {
- cleanup_wasm_objects();
- return 0;
- }
-
- wasm_imports = (wasm_extern_vec_t) WASM_EMPTY_VEC;
- if (!link_wasm_imports(linkable_functions, wasm_module)) {
- return 0;
- }
-
- wasm_trap_t* traps = NULL;
-
- wasm_instance = wasm_instance_new(wasm_store, wasm_module, &wasm_imports, &traps);
- if (!wasm_instance) {
- cleanup_wasm_objects();
- return 0;
- }
-
- wasm_runtime.wasm_engine = wasm_engine;
- wasm_runtime.wasm_module = wasm_module;
- wasm_runtime.wasm_imports = wasm_imports;
- wasm_runtime.wasm_memory = wasm_memory;
- wasm_runtime.wasm_instance = wasm_instance;
-
- wasm_runtime.argc = argc;
- wasm_runtime.argv = argv;
-
- wasm_extern_t* start_extern = wasm_extern_lookup_by_name(wasm_module, wasm_instance, "_start");
- wasm_func_t* start_func = wasm_extern_as_func(start_extern);
-
- wasm_val_vec_t args;
- wasm_val_vec_t results;
- wasm_val_vec_new_uninitialized(&args, 0);
- wasm_val_vec_new_uninitialized(&results, 1);
-
- wasm_trap_t *run_trap = wasm_func_call(start_func, &args, &results);
-
-#if 1
- if (run_trap != NULL) onyx_print_trap(run_trap);
-#endif
-
- goto end;
-
- bh_printf("An error occured trying to run the WASM module...\n");
-
-#ifndef USE_OVM_DEBUGGER
- i32 len = wasmer_last_error_length();
- char *buf = alloca(len + 1);
- wasmer_last_error_message(buf, len);
- bh_printf("%b\n", buf, len);
-#endif
-
-end:
- cleanup_wasm_objects();
- return run_trap == NULL;
-}
+++ /dev/null
-// This file is directly included in src/onxywasm.c
-// It is here purely to decrease the amount of clutter in the main file.
-
-typedef struct StructMethodData {
- u32 name_loc;
- u32 name_len;
- u32 type;
- u32 data_loc;
-} StructMethodData;
-
-static 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))
-#define WRITE_PTR(val) \
- bh_buffer_align(&table_buffer, POINTER_SIZE); \
- PATCH; \
- if (POINTER_SIZE == 4) bh_buffer_write_u32(&table_buffer, val); \
- if (POINTER_SIZE == 8) bh_buffer_write_u64(&table_buffer, val);
-#define WRITE_SLICE(ptr, count) \
- WRITE_PTR(ptr); \
- if (POINTER_SIZE == 4) bh_buffer_write_u32(&table_buffer, count); \
- if (POINTER_SIZE == 8) bh_buffer_write_u64(&table_buffer, count);
-
- // This is the data behind the "type_table" slice in runtime/info/types.onyx
- #if (POINTER_SIZE == 4)
- #define Table_Info_Type u32
- #else
- #define Table_Info_Type u64
- #endif
- u32 type_count = bh_arr_length(type_map.entries) + 1;
- Table_Info_Type* table_info = bh_alloc_array(global_heap_allocator, Table_Info_Type, type_count); // HACK
- memset(table_info, 0, type_count * sizeof(Table_Info_Type));
-
- bh_buffer table_buffer;
- bh_buffer_init(&table_buffer, global_heap_allocator, 4096);
-
- u32 type_table_info_data_id = NEXT_DATA_ID(module);
-
- ConstExprContext constexpr_ctx;
- constexpr_ctx.module = module;
- constexpr_ctx.data_id = type_table_info_data_id;
-
- // 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.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.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.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));
- WRITE_SLICE(components_base, 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);
-
- WRITE_SLICE(parameters_base, 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);
- WRITE_SLICE(name_loc, (*value)->token->length);
-
- assert((*value)->value->kind == Ast_Kind_NumLit);
- AstNumLit *num = (AstNumLit *) (*value)->value;
- bh_buffer_write_u64(&table_buffer, num->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);
- WRITE_SLICE(name_base, name_length);
- WRITE_SLICE(member_base, 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);
- u32* meta_locations = bh_alloc_array(global_scratch_allocator, u32, s->mem_count);
- u32* struct_tag_locations = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(s->meta_tags));
- memset(value_locations, 0, s->mem_count * sizeof(u32));
- memset(meta_locations, 0, s->mem_count * sizeof(u32));
- memset(struct_tag_locations, 0, bh_arr_length(s->meta_tags) * sizeof(u32));
-
- // Member names
- 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);
-
- // Polymorphic solutions
- 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);
- constexpr_ctx.data = table_buffer.data;
- if (emit_constexpr_(&constexpr_ctx, sln->value, table_buffer.length)) {
- table_buffer.length += size;
- break;
- }
-
- // fallthrough
- }
-
- default: {
- // Set to null if this is not known how to encode
- param_locations[i-1] = 0;
- break;
- }
- }
- }
-
- bh_buffer_align(&table_buffer, 8);
-
- // Member default values
- 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);
- constexpr_ctx.data = table_buffer.data;
- if (!emit_constexpr_(&constexpr_ctx, value, table_buffer.length)) {
- // 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;
- }
- }
-
- // Member tags
- i = 0;
- bh_arr_each(StructMember*, pmem, s->memarr) {
- StructMember* mem = *pmem;
-
- if (mem->meta_tags == NULL) {
- i += 1;
- continue;
- }
-
- bh_arr(AstTyped *) meta_tags = mem->meta_tags;
- assert(meta_tags);
-
- bh_arr(u64) meta_tag_locations=NULL;
- bh_arr_new(global_heap_allocator, meta_tag_locations, bh_arr_length(meta_tags));
-
- int j = 0;
- bh_arr_each(AstTyped *, meta, meta_tags) {
- AstTyped* value = *meta;
- assert(value->flags & Ast_Flag_Comptime);
- assert(value->type);
-
- u32 size = type_size_of(value->type);
- bh_buffer_align(&table_buffer, type_alignment_of(value->type));
- meta_tag_locations[j] = table_buffer.length;
-
- bh_buffer_grow(&table_buffer, table_buffer.length + size);
- constexpr_ctx.data = table_buffer.data;
- assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
- table_buffer.length += size;
-
- j += 1;
- }
-
- bh_buffer_align(&table_buffer, 8);
- meta_locations[i] = table_buffer.length;
-
- fori (k, 0, bh_arr_length(meta_tags)) {
- WRITE_SLICE(meta_tag_locations[k], meta_tags[k]->type->id);
- }
-
- bh_arr_free(meta_tag_locations);
- i += 1;
- }
-
- bh_buffer_align(&table_buffer, 8);
- u32 members_base = table_buffer.length;
-
- // Member array
- i = 0;
- bh_arr_each(StructMember*, pmem, s->memarr) {
- StructMember* mem = *pmem;
-
- u32 name_loc = name_locations[i];
- u32 value_loc = value_locations[i];
- u32 meta_loc = meta_locations[i++];
-
- WRITE_SLICE(name_loc, 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);
-
- WRITE_PTR(value_loc);
-
- WRITE_SLICE(meta_loc, bh_arr_length(mem->meta_tags));
- }
-
- bh_buffer_align(&table_buffer, 8);
- u32 params_base = table_buffer.length;
-
- // Polymorphic solution any array
- i = 0;
- bh_arr_each(AstPolySolution, sln, s->poly_sln) {
- WRITE_PTR(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);
- }
-
- // Struct tag array
- i = 0;
- bh_arr_each(AstTyped *, tag, s->meta_tags) {
- AstTyped* value = *tag;
- assert(value->flags & Ast_Flag_Comptime);
- assert(value->type);
-
- u32 size = type_size_of(value->type);
- bh_buffer_align(&table_buffer, type_alignment_of(value->type));
- struct_tag_locations[i] = table_buffer.length;
-
- bh_buffer_grow(&table_buffer, table_buffer.length + size);
- constexpr_ctx.data = table_buffer.data;
- assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
- table_buffer.length += size;
-
- i += 1;
- }
-
- // Struct methods
- bh_arr(StructMethodData) method_data=NULL;
- AstType *ast_type = type->ast_type;
- if (ast_type && ast_type->kind == Ast_Kind_Struct_Type) {
- AstStructType *struct_type = (AstStructType *) ast_type;
- Scope* struct_scope = struct_type->scope;
-
- if (struct_scope == NULL) goto no_methods;
-
- fori (i, 0, shlen(struct_scope->symbols)) {
- AstFunction* node = (AstFunction *) strip_aliases(struct_scope->symbols[i].value);
- if (node->kind != Ast_Kind_Function) continue;
- assert(node->entity);
- assert(node->entity->function == node);
-
- // Name
- char *name = struct_scope->symbols[i].key;
- u32 name_loc = table_buffer.length;
- u32 name_len = strlen(name);
- bh_buffer_append(&table_buffer, name, name_len);
-
- // any data member
- bh_buffer_align(&table_buffer, 4);
- u32 data_loc = table_buffer.length;
- u32 func_idx = get_element_idx(module, node);
- bh_buffer_write_u32(&table_buffer, func_idx);
-
- bh_arr_push(method_data, ((StructMethodData) {
- .name_loc = name_loc,
- .name_len = name_len,
- .type = node->type->id,
- .data_loc = data_loc,
- }));
- }
- }
-
- no_methods:
-
- bh_buffer_align(&table_buffer, 4);
- u32 method_data_base = table_buffer.length;
-
- i = 0;
- bh_arr_each(StructMethodData, method, method_data) {
- WRITE_SLICE(method->name_loc, method->name_len);
- WRITE_PTR(method->data_loc);
- bh_buffer_write_u32(&table_buffer, method->type);
- }
-
- bh_buffer_align(&table_buffer, 8);
- u32 struct_tag_base = table_buffer.length;
-
- fori (i, 0, bh_arr_length(s->meta_tags)) {
- WRITE_SLICE(struct_tag_locations[i], s->meta_tags[i]->type->id);
- }
-
- // Struct name
- 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));
-
- if (type->Struct.constructed_from != NULL) {
- bh_buffer_write_u32(&table_buffer, type->Struct.constructed_from->type_id);
- } else {
- bh_buffer_write_u32(&table_buffer, 0);
- }
-
- WRITE_SLICE(name_base, name_length);
- WRITE_SLICE(members_base, s->mem_count);
- WRITE_SLICE(params_base, bh_arr_length(s->poly_sln));
- WRITE_SLICE(struct_tag_base, bh_arr_length(s->meta_tags));
- WRITE_SLICE(method_data_base, bh_arr_length(method_data));
-
- bh_arr_free(method_data);
- break;
- }
-
- case Type_Kind_PolyStruct: {
- u32* tag_locations = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(type->PolyStruct.meta_tags));
- memset(tag_locations, 0, sizeof(u32) * bh_arr_length(type->PolyStruct.meta_tags));
-
- u32 name_base = table_buffer.length;
- u32 name_length = strlen(type->PolyStruct.name);
- bh_buffer_append(&table_buffer, type->PolyStruct.name, name_length);
-
- u32 tags_count = bh_arr_length(type->PolyStruct.meta_tags);
- i32 i = 0;
- bh_arr_each(AstTyped *, tag, type->PolyStruct.meta_tags) {
- AstTyped* value = *tag;
-
- // Polymorphic structs are weird in this case, because the tag might not be constructed generically for
- // the polymorphic structure so it should only be constructed for actual solidified structures.
- // See core/containers/map.onyx with Custom_Format for an example.
- if (!(value->flags & Ast_Flag_Comptime)) {
- tags_count--;
- continue;
- }
-
- assert(value->type);
-
- u32 size = type_size_of(value->type);
- bh_buffer_align(&table_buffer, type_alignment_of(value->type));
- tag_locations[i] = table_buffer.length;
-
- bh_buffer_grow(&table_buffer, table_buffer.length + size);
-
- constexpr_ctx.data = table_buffer.data;
- assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
- table_buffer.length += size;
-
- i += 1;
- }
-
- bh_buffer_align(&table_buffer, 8);
- u32 tags_base = table_buffer.length;
-
- fori (i, 0, tags_count) {
- WRITE_SLICE(tag_locations[i], type->PolyStruct.meta_tags[i]->type->id);
- }
-
- 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, 0);
- bh_buffer_write_u32(&table_buffer, 0);
- WRITE_SLICE(name_base, name_length);
- WRITE_SLICE(tags_base, tags_count);
-
- break;
- }
-
- case Type_Kind_Distinct: {
- u32 name_base = table_buffer.length;
- u32 name_length = strlen(type->Distinct.name);
- bh_buffer_append(&table_buffer, type->Distinct.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->Distinct.base_type->id);
- WRITE_SLICE(name_base, name_length);
- break;
- }
- }
- }
-
- if (context.options->verbose_output == 1) {
- bh_printf("Type table size: %d bytes.\n", table_buffer.length);
- }
-
- WasmDatum type_info_data = {
- .alignment = 8,
- .length = table_buffer.length,
- .data = table_buffer.data,
- };
- emit_data_entry(module, &type_info_data);
- assert(type_info_data.id == type_table_info_data_id);
-
- bh_arr_each(u32, patch_loc, base_patch_locations) {
- DatumPatchInfo patch;
- patch.kind = Datum_Patch_Relative;
- patch.data_id = type_info_data.id;
- patch.offset = 0;
- patch.index = type_info_data.id;
- patch.location = *patch_loc;
- bh_arr_push(module->data_patches, patch);
- }
-
- WasmDatum type_table_data = {
- .alignment = POINTER_SIZE,
- .length = type_count * POINTER_SIZE,
- .data = table_info,
- };
- emit_data_entry(module, &type_table_data);
-
- fori (i, 0, type_count) {
- DatumPatchInfo patch;
- patch.kind = Datum_Patch_Data;
- patch.data_id = type_info_data.id;
- patch.offset = table_info[i];
- patch.index = type_table_data.id;
- patch.location = i * POINTER_SIZE;
- bh_arr_push(module->data_patches, patch);
- }
-
- Table_Info_Type* tmp_data = bh_alloc(global_heap_allocator, 2 * POINTER_SIZE);
- tmp_data[0] = 0;
- tmp_data[1] = type_count;
- WasmDatum type_table_global_data = {
- .alignment = POINTER_SIZE,
- .length = 2 * POINTER_SIZE,
- .data = tmp_data,
- };
- emit_data_entry(module, &type_table_global_data);
-
- {
- DatumPatchInfo patch;
- patch.kind = Datum_Patch_Data;
- patch.data_id = type_table_data.id;
- patch.offset = 0;
- patch.index = type_table_global_data.id;
- patch.location = 0;
- bh_arr_push(module->data_patches, patch);
- }
-
- return type_table_global_data.id;
-
-#undef WRITE_SLICE
-#undef WRITE_PTR
-#undef PATCH
-}
-
-
-
-static u64 build_foreign_blocks(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, foreign_buffer.length))
-#define WRITE_PTR(val) \
- bh_buffer_align(&foreign_buffer, POINTER_SIZE); \
- PATCH; \
- if (POINTER_SIZE == 4) bh_buffer_write_u32(&foreign_buffer, val); \
- if (POINTER_SIZE == 8) bh_buffer_write_u64(&foreign_buffer, val);
-#define WRITE_SLICE(ptr, count) \
- WRITE_PTR(ptr); \
- if (POINTER_SIZE == 4) bh_buffer_write_u32(&foreign_buffer, count); \
- if (POINTER_SIZE == 8) bh_buffer_write_u64(&foreign_buffer, count);
-
- // This is the data behind the "type_table" slice in type_info.onyx
- #if (POINTER_SIZE == 4)
- #define Foreign_Block_Type u32
- #else
- #define Foreign_Block_Type u64
- #endif
- u32 block_count = bh_arr_length(module->foreign_blocks);
- Foreign_Block_Type* foreign_info = bh_alloc_array(global_heap_allocator, Foreign_Block_Type, block_count); // HACK
- memset(foreign_info, 0, block_count * sizeof(Foreign_Block_Type));
-
- bh_buffer foreign_buffer;
- bh_buffer_init(&foreign_buffer, global_heap_allocator, 4096);
-
- //
- // This is necessary because 0 is an invalid offset to store in this
- // buffer, as 0 will map to NULL. This could be a single byte insertion,
- // but 64 bytes keeps better alignment.
- bh_buffer_write_u64(&foreign_buffer, 0);
-
- u32 index = 0;
- bh_arr_each(AstForeignBlock *, pfb, module->foreign_blocks) {
- AstForeignBlock *fb = *pfb;
-
- u32 funcs_length = 0;
-
- u32 *name_offsets = bh_alloc_array(global_scratch_allocator, u32, shlen(fb->scope->symbols));
- u32 *name_lengths = bh_alloc_array(global_scratch_allocator, u32, shlen(fb->scope->symbols));
- u32 *func_types = bh_alloc_array(global_scratch_allocator, u32, shlen(fb->scope->symbols));
-
- fori (i, 0, shlen(fb->scope->symbols)) {
- AstFunction *func = (AstFunction *) fb->scope->symbols[i].value;
- if (func->kind != Ast_Kind_Function) continue;
-
- u32 func_name_base = foreign_buffer.length;
- u32 func_name_length = func->foreign_name->length;
- bh_buffer_append(&foreign_buffer, func->foreign_name->text, func_name_length);
-
- name_offsets[funcs_length] = func_name_base;
- name_lengths[funcs_length] = func_name_length;
- func_types[funcs_length] = func->type->id;
- funcs_length++;
- }
-
- bh_buffer_align(&foreign_buffer, 8);
- u32 funcs_base = foreign_buffer.length;
-
- fori (i, 0, (i64) funcs_length) {
- bh_buffer_align(&foreign_buffer, POINTER_SIZE);
- WRITE_SLICE(name_offsets[i], name_lengths[i]);
- bh_buffer_write_u32(&foreign_buffer, func_types[i]);
- }
-
- u32 name_base = foreign_buffer.length;
- u32 name_length = fb->module_name->length;
- bh_buffer_append(&foreign_buffer, fb->module_name->text, name_length);
- bh_buffer_align(&foreign_buffer, 8);
-
- foreign_info[index] = foreign_buffer.length;
- WRITE_SLICE(name_base, name_length);
- WRITE_SLICE(funcs_base, funcs_length);
- index++;
- }
-
-
- if (context.options->verbose_output == 1) {
- bh_printf("Foreign blocks size: %d bytes.\n", foreign_buffer.length);
- }
-
- WasmDatum foreign_info_data = {
- .alignment = 8,
- .length = foreign_buffer.length,
- .data = foreign_buffer.data,
- };
- emit_data_entry(module, &foreign_info_data);
-
- bh_arr_each(u32, patch_loc, base_patch_locations) {
- DatumPatchInfo patch;
- patch.kind = Datum_Patch_Relative;
- patch.data_id = foreign_info_data.id;
- patch.offset = 0;
- patch.index = foreign_info_data.id;
- patch.location = *patch_loc;
- bh_arr_push(module->data_patches, patch);
- }
-
- WasmDatum foreign_table_data = {
- .alignment = POINTER_SIZE,
- .length = block_count * POINTER_SIZE,
- .data = foreign_info,
- };
- emit_data_entry(module, &foreign_table_data);
-
- fori (i, 0, block_count) {
- DatumPatchInfo patch;
- patch.kind = Datum_Patch_Data;
- patch.data_id = foreign_info_data.id;
- patch.offset = foreign_info[i];
- patch.index = foreign_table_data.id;
- patch.location = i * POINTER_SIZE;
- bh_arr_push(module->data_patches, patch);
- }
-
- Foreign_Block_Type* tmp_data = bh_alloc(global_heap_allocator, 2 * POINTER_SIZE);
- tmp_data[0] = 0;
- tmp_data[1] = block_count;
- WasmDatum foreign_table_global_data = {
- .alignment = POINTER_SIZE,
- .length = 2 * POINTER_SIZE,
- .data = tmp_data,
- };
- emit_data_entry(module, &foreign_table_global_data);
-
- {
- DatumPatchInfo patch;
- patch.kind = Datum_Patch_Data;
- patch.data_id = foreign_table_data.id;
- patch.location = 0;
- patch.index = foreign_table_global_data.id;
- patch.offset = 0;
- bh_arr_push(module->data_patches, patch);
- }
-
- return foreign_table_global_data.id;
-
-#undef WRITE_SLICE
-#undef WRITE_PTR
-#undef PATCH
-}
-
-
-
-static u64 build_tagged_procedures(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, tag_proc_buffer.length))
-#define WRITE_PTR(val) \
- bh_buffer_align(&tag_proc_buffer, POINTER_SIZE); \
- PATCH; \
- if (POINTER_SIZE == 4) bh_buffer_write_u32(&tag_proc_buffer, val); \
- if (POINTER_SIZE == 8) bh_buffer_write_u64(&tag_proc_buffer, val);
-#define WRITE_SLICE(ptr, count) \
- WRITE_PTR(ptr); \
- if (POINTER_SIZE == 4) bh_buffer_write_u32(&tag_proc_buffer, count); \
- if (POINTER_SIZE == 8) bh_buffer_write_u64(&tag_proc_buffer, count);
-
- #if (POINTER_SIZE == 4)
- #define Tagged_Procedure_Type u32
- #else
- #define Tagged_Procedure_Type u64
- #endif
- u32 proc_count = bh_arr_length(module->procedures_with_tags);
- Tagged_Procedure_Type* tag_proc_info = bh_alloc_array(global_heap_allocator, Tagged_Procedure_Type, proc_count); // HACK
- memset(tag_proc_info, 0, proc_count * sizeof(Tagged_Procedure_Type));
-
- bh_buffer tag_proc_buffer;
- bh_buffer_init(&tag_proc_buffer, global_heap_allocator, 4096);
-
- u32 proc_info_data_id = NEXT_DATA_ID(module);
-
- ConstExprContext constexpr_ctx;
- constexpr_ctx.module = module;
- constexpr_ctx.data_id = proc_info_data_id;
-
- //
- // This is necessary because 0 is an invalid offset to store in this
- // buffer, as 0 will map to NULL. This could be a single byte insertion,
- // but 64 bytes keeps better alignment.
- bh_buffer_write_u64(&tag_proc_buffer, 0);
-
- u32 index = 0;
- bh_arr_each(AstFunction *, pfunc, module->procedures_with_tags) {
- AstFunction *func = *pfunc;
- if (!should_emit_function(func)) {
- proc_count--;
- continue;
- }
-
- u32 tag_count = bh_arr_length(func->tags);
- u32 *tag_data_offsets = bh_alloc_array(global_scratch_allocator, u32, tag_count);
- u32 *tag_data_types = bh_alloc_array(global_scratch_allocator, u32, tag_count);
-
- u32 tag_index = 0;
- bh_arr_each(AstTyped *, ptag, func->tags) {
- AstTyped *tag = *ptag;
- bh_buffer_align(&tag_proc_buffer, type_alignment_of(tag->type));
-
- tag_data_offsets[tag_index ] = tag_proc_buffer.length;
- tag_data_types [tag_index++] = tag->type->id;
-
- u32 size = type_size_of(tag->type);
- bh_buffer_grow(&tag_proc_buffer, tag_proc_buffer.length + size);
-
- constexpr_ctx.data = tag_proc_buffer.data;
- emit_constexpr(&constexpr_ctx, tag, tag_proc_buffer.length);
- tag_proc_buffer.length += size;
- }
-
- bh_buffer_align(&tag_proc_buffer, 4);
- u32 tag_array_base = tag_proc_buffer.length;
- fori (i, 0, tag_count) {
- PATCH;
- bh_buffer_write_u32(&tag_proc_buffer, tag_data_offsets[i]);
- bh_buffer_write_u32(&tag_proc_buffer, tag_data_types[i]);
- }
-
- bh_buffer_align(&tag_proc_buffer, 4);
- tag_proc_info[index++] = tag_proc_buffer.length;
-
- bh_buffer_write_u32(&tag_proc_buffer, get_element_idx(module, func));
- bh_buffer_write_u32(&tag_proc_buffer, func->type->id);
- WRITE_SLICE(tag_array_base, tag_count);
- }
-
- if (context.options->verbose_output == 1) {
- bh_printf("Tagged procedure size: %d bytes.\n", tag_proc_buffer.length);
- }
-
- WasmDatum proc_info_data = {
- .alignment = 8,
- .length = tag_proc_buffer.length,
- .data = tag_proc_buffer.data,
- };
- emit_data_entry(module, &proc_info_data);
- assert(proc_info_data.id == proc_info_data_id);
-
- bh_arr_each(u32, patch_loc, base_patch_locations) {
- DatumPatchInfo patch;
- patch.kind = Datum_Patch_Relative;
- patch.data_id = proc_info_data.id;
- patch.location = *patch_loc;
- patch.index = proc_info_data.id;
- patch.offset = 0;
- bh_arr_push(module->data_patches, patch);
- }
-
- WasmDatum proc_table_data = {
- .alignment = POINTER_SIZE,
- .length = proc_count * POINTER_SIZE,
- .data = tag_proc_info,
- };
- emit_data_entry(module, &proc_table_data);
-
- fori (i, 0, proc_count) {
- DatumPatchInfo patch;
- patch.kind = Datum_Patch_Data;
- patch.data_id = proc_info_data.id;
- patch.offset = tag_proc_info[i];
- patch.index = proc_table_data.id;
- patch.location = i * POINTER_SIZE;
- bh_arr_push(module->data_patches, patch);
- }
-
- Tagged_Procedure_Type* tmp_data = bh_alloc(global_heap_allocator, 2 * POINTER_SIZE);
- tmp_data[0] = 0;
- tmp_data[1] = proc_count;
- WasmDatum proc_table_global_data = {
- .alignment = POINTER_SIZE,
- .length = 2 * POINTER_SIZE,
- .data = tmp_data,
- };
- emit_data_entry(module, &proc_table_global_data);
-
- {
- DatumPatchInfo patch;
- patch.kind = Datum_Patch_Data;
- patch.offset = 0;
- patch.data_id = proc_table_data.id;
- patch.index = proc_table_global_data.id;
- patch.location = 0;
- bh_arr_push(module->data_patches, patch);
- }
-
- return proc_table_global_data.id;
-
-#undef WRITE_SLICE
-#undef WRITE_PTR
-#undef PATCH
-}
-