From b0118765975fe5aa37bf3944d2fb5700662c27fd Mon Sep 17 00:00:00 2001 From: Ge Wang Date: Sat, 16 Nov 2024 23:39:27 -0800 Subject: [PATCH] vec2/3/4 member function call from literal values #special-primitive-member-func-from-literal --- VERSIONS | 6 + src/core/chuck_absyn.h | 2 +- src/core/chuck_emit.cpp | 215 +++++++++++++-------- src/core/chuck_instr.cpp | 61 +++++- src/core/chuck_instr.h | 19 +- src/core/chuck_type.cpp | 13 +- src/test/01-Basic/262-vec-literal-func.ck | 37 ++++ src/test/01-Basic/263-vec-literal-stack.ck | 24 +++ 8 files changed, 276 insertions(+), 101 deletions(-) create mode 100644 src/test/01-Basic/262-vec-literal-func.ck create mode 100644 src/test/01-Basic/263-vec-literal-stack.ck diff --git a/VERSIONS b/VERSIONS index dc86f8017..8f20ad5df 100644 --- a/VERSIONS +++ b/VERSIONS @@ -18,8 +18,14 @@ ChucK VERSIONS log various scenarios. (fixed) long-standing SndBuf issue, especially prevalent on macOS: "System error : Too many open files." this was due a combination of +(fixed) memory leak involving string operations (e.g., string + string); + internally, string operations now are appropriately handled by the + operator overloading mechanism +(fixed) < <= > >= operators for strings (fixed) vec2/vec3/vec4 implicit cast logic for +=> and -=> operations (previously, this would cause a crash in some cases) +(fixed) vec2/vec3/vec4 function call from literal (non-variable) value + (e.g., @(1,0).magnitude(); previously this yielded a compiler error) (added) vector dot product methods for vec2, vec3, and vec4 float vec2.dot( vec2 rhs ) float vec3.dot( vec3 rhs ) diff --git a/src/core/chuck_absyn.h b/src/core/chuck_absyn.h index 813c54186..da6edda89 100644 --- a/src/core/chuck_absyn.h +++ b/src/core/chuck_absyn.h @@ -336,7 +336,7 @@ struct a_Exp_Dur_ { a_Exp base; a_Exp unit; uint32_t line; uint32_t where; a_Exp struct a_Exp_Array_ { a_Exp base; a_Array_Sub indices; uint32_t line; uint32_t where; a_Exp self; }; struct a_Exp_Func_Call_ { a_Exp func; a_Exp args; t_CKTYPE ret_type; t_CKFUNC ck_func; t_CKVMCODE ck_vm_code; uint32_t line; uint32_t where; a_Exp self; }; -struct a_Exp_Dot_Member_ { a_Exp base; t_CKTYPE t_base; S_Symbol xid; uint32_t line; uint32_t where; a_Exp self; }; +struct a_Exp_Dot_Member_ { a_Exp base; t_CKTYPE t_base; S_Symbol xid; t_CKBOOL isSpecialPrimitiveFunc; uint32_t line; uint32_t where; a_Exp self; }; struct a_Exp_If_ { a_Exp cond; a_Exp if_exp; a_Exp else_exp; uint32_t line; uint32_t where; a_Exp self; }; struct a_Exp_Decl_ { a_Type_Decl type; a_Var_Decl_List var_decl_list; int num_var_decls; int is_static; int is_global; int is_const; t_CKTYPE ck_type; int is_auto; uint32_t line; uint32_t where; a_Exp self; }; diff --git a/src/core/chuck_emit.cpp b/src/core/chuck_emit.cpp index 2059a76db..05696237f 100644 --- a/src/core/chuck_emit.cpp +++ b/src/core/chuck_emit.cpp @@ -40,10 +40,11 @@ #include "util_string.h" // added 1.5.0.5 #include #include - using namespace std; +// forward references +struct Chuck_FuncCall_Options; //----------------------------------------------------------------------------- @@ -81,7 +82,8 @@ t_CKBOOL emit_engine_emit_exp_dur( Chuck_Emitter * emit, a_Exp_Dur dur ); t_CKBOOL emit_engine_emit_exp_array( Chuck_Emitter * emit, a_Exp_Array array ); t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit, Chuck_Func * func, Chuck_Type * type, t_CKUINT line, t_CKUINT where, - t_CKBOOL spork = FALSE ); + t_CKBOOL spork = FALSE, + Chuck_FuncCall_Options * options = NULL ); t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit, a_Exp_Func_Call func_call, t_CKBOOL spork = FALSE ); t_CKBOOL emit_engine_emit_func_args( Chuck_Emitter * emit, a_Exp_Func_Call func_call ); @@ -4209,20 +4211,123 @@ t_CKBOOL emit_engine_emit_exp_array( Chuck_Emitter * emit, a_Exp_Array array ) //----------------------------------------------------------------------------- -// name: emit_engine_emit_exp_func_call() +// name: struct Chuck_FuncCall_Options | 1.5.4.2 (ge) added +// desc: a struct containing func call options, usually for specific cases +//----------------------------------------------------------------------------- +struct Chuck_FuncCall_Options +{ + // set this to true ONLY for specific cases of... + // 1) a special primitive (e.g., vec2/3/4)... + // 2) calls one of its "member" functions (e.g., magnitude()) + // 3) not from a variable but from a literal value (e.g., @(1,2,3).magnitude()) + // in these cases, a special Chuck_Instr_Reg_Transmute_Value_To_Pointer + // must have been emitted prior to the member function call + // NOTE: in effect, this option tells the member function call to + // clean up the trasmuted THIS pointer before returning + // NOTE: part of #special-primitive-member-func-from-literal + t_CKBOOL transmutingSpecialPrimitiveForMemberFunc; + + // constructor + Chuck_FuncCall_Options( t_CKBOOL transmuting = FALSE ) + { + // defaults + transmutingSpecialPrimitiveForMemberFunc = transmuting; + } +}; + + + + +//----------------------------------------------------------------------------- +// name: emit_engine_emit_func_args() // desc: ... //----------------------------------------------------------------------------- +t_CKBOOL emit_engine_emit_func_args( Chuck_Emitter * emit, + a_Exp_Func_Call func_call ) +{ + // emit the args (TRUE for doAddRef added 1.3.0.0) + if( !emit_engine_emit_exp( emit, func_call->args, TRUE ) ) + { + EM_error2( func_call->where, + "(emit): internal error in emitting function call arguments..." ); + return FALSE; + } + + return TRUE; +} + + + + +//----------------------------------------------------------------------------- +// name: emit_engine_emit_exp_func_call() +// desc: emit function call from a_Exp_Func_Call +//----------------------------------------------------------------------------- +t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit, + a_Exp_Func_Call func_call, + t_CKBOOL spork ) +{ + // note: spork situations are now taken care in exp_spork... + // please look at that one before modifying this one! + + // make sure there are args, and not sporking + if( func_call->args && !spork ) + { + if( !emit_engine_emit_func_args( emit, func_call ) ) + return FALSE; + } + + // emit func + if( !emit_engine_emit_exp( emit, func_call->func ) ) + { + EM_error2( func_call->where, + "(emit): internal error in evaluating function call..." ); + return FALSE; + } + + // line and pos + t_CKUINT line = func_call->line; + t_CKUINT where = func_call->where; + // additional func call options | 1.5.4.2 (ge) added + Chuck_FuncCall_Options options; + + // in the case of member func calls + if( func_call->func->s_type == ae_exp_dot_member ) + { + // if possible, get more accurate code position + line = func_call->func->dot_member.line; + where = func_call->func->dot_member.where; + // set option | 1.5.4.2 (ge) added as part of #special-primitive-member-func-from-literal + // this should only be true for special non-primitive functions *from literal value* e.g., @(1,2,3).magnitude(); + options.transmutingSpecialPrimitiveForMemberFunc = func_call->func->dot_member.isSpecialPrimitiveFunc; + } + + // the rest + return emit_engine_emit_exp_func_call( emit, func_call->ck_func, func_call->ret_type, + line, where, spork, &options ); +} + + + + +//----------------------------------------------------------------------------- +// name: emit_engine_emit_exp_func_call() +// desc: emit function call from necessary information +//----------------------------------------------------------------------------- t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit, Chuck_Func * func, Chuck_Type * type, t_CKUINT line, t_CKUINT where, - t_CKBOOL spork ) + t_CKBOOL spork, + Chuck_FuncCall_Options * options ) { // is a member? t_CKBOOL is_member = func->is_member; // is a static? (within class) t_CKBOOL is_static = func->is_static; + // whether to enable the #special-primitive-member-func-from-literal option for member func + t_CKBOOL transmuting = options ? options->transmutingSpecialPrimitiveForMemberFunc : FALSE; // only check dependency violations if we are at a context-top-level // or class-top-level scope, i.e., not in a function definition @@ -4275,7 +4380,7 @@ t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit, { // is member (1.3.1.0: changed to use kind instead of size) if( is_member ) - emit->append( instr = new Chuck_Instr_Func_Call_Member( kind, func ) ); + emit->append( instr = new Chuck_Instr_Func_Call_Member( kind, func, CK_FUNC_CALL_THIS_IN_BACK, transmuting ) ); else if( is_static ) emit->append( instr = new Chuck_Instr_Func_Call_Static( kind, func ) ); else // 1.5.1.5 (ge & andrew) new planes of existence --> this is in global-scope (not global variable) @@ -4302,71 +4407,6 @@ t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit, -//----------------------------------------------------------------------------- -// name: emit_engine_emit_func_args() -// desc: ... -//----------------------------------------------------------------------------- -t_CKBOOL emit_engine_emit_func_args( Chuck_Emitter * emit, - a_Exp_Func_Call func_call ) -{ - // emit the args (TRUE for doAddRef added 1.3.0.0) - if( !emit_engine_emit_exp( emit, func_call->args, TRUE ) ) - { - EM_error2( func_call->where, - "(emit): internal error in emitting function call arguments..." ); - return FALSE; - } - - return TRUE; -} - - - - -//----------------------------------------------------------------------------- -// name: emit_engine_emit_exp_func_call() -// desc: emit function call from a_Exp_Func_Call -//----------------------------------------------------------------------------- -t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit, - a_Exp_Func_Call func_call, - t_CKBOOL spork ) -{ - // note: spork situations are now taken care in exp_spork... - // please look at that one before modifying this one! - - // make sure there are args, and not sporking - if( func_call->args && !spork ) - { - if( !emit_engine_emit_func_args( emit, func_call ) ) - return FALSE; - } - - // emit func - if( !emit_engine_emit_exp( emit, func_call->func ) ) - { - EM_error2( func_call->where, - "(emit): internal error in evaluating function call..." ); - return FALSE; - } - - // line and pos - t_CKUINT line = func_call->line; - t_CKUINT where = func_call->where; - // if possible, get more accurate code position - if( func_call->func->s_type == ae_exp_dot_member ) - { - line = func_call->func->dot_member.line; - where = func_call->func->dot_member.where; - } - - // the rest - return emit_engine_emit_exp_func_call( emit, func_call->ck_func, func_call->ret_type, - line, where, spork ); -} - - - - //----------------------------------------------------------------------------- // name: emit_engine_emit_dot_member_special() // desc: emit special dot member: complex, polar, vec2, vec3, vec4 @@ -4546,23 +4586,40 @@ t_CKBOOL emit_engine_emit_exp_dot_member_special( Chuck_Emitter * emit, // get the func value = type_engine_find_value( t_base, member->xid ); func = value->func_ref; - // make sure it's there - assert( func != NULL ); + if( !func ) + { + // should not get here + EM_error2( member->base->where, + "(emit): internal error in lit_special(): expected function not found in value" ); + // done + return FALSE; + } // NOTE: base already emitted earier in this function (and as var) // check base; 1.3.5.3 if( member->base->s_meta == ae_meta_value ) // is literal { - // dup the value as pointer (as faux-'this' pointer) - emit->append( new Chuck_Instr_Reg_Dup_Last_As_Pointer( t_base->size / sz_WORD ) ); - } - else // normal object - { - // dup the base pointer ('this' pointer as argument -- special case primitive) - emit->append( new Chuck_Instr_Reg_Dup_Last ); + // verify + if( !member->isSpecialPrimitiveFunc ) + { + // should not get here + EM_error2( member->base->where, + "(emit): internal error in lit_special(): unexpected specialPrimitiveFunc == FALSE" ); + // done + return FALSE; + } + + // 1.5.4.2 (ge) #special-primitive-member-func-from-literal + // transmute the value as pointer (as faux-'this' pointer) + emit->append( new Chuck_Instr_Reg_Transmute_Value_To_Pointer( t_base->size ) ); } + // dup the base pointer ('this' pointer as argument -- special case primitive) + // as of 1.5.4.2 this is emitted for both literals and variables + // #special-primitive-member-func-from-literal + emit->append( new Chuck_Instr_Reg_Dup_Last ); + // find the offset for virtual table offset = func->vt_index; // emit the function diff --git a/src/core/chuck_instr.cpp b/src/core/chuck_instr.cpp index 525e7685d..4177a65df 100644 --- a/src/core/chuck_instr.cpp +++ b/src/core/chuck_instr.cpp @@ -1965,6 +1965,7 @@ void Chuck_Instr_vec4_Divide_float_Assign::execute( Chuck_VM * vm, Chuck_VM_Shre + #pragma mark === String Arithmetic === @@ -2474,17 +2475,41 @@ void Chuck_Instr_Reg_Dup_Last2::execute( Chuck_VM * vm, Chuck_VM_Shred * shred ) //----------------------------------------------------------------------------- -// name: execute() -// desc: ... +// allocate memory in special-primitive storage; used for vec2/3/4 member function calls +// 1.5.4.2 (ge) added as part of #special-primitive-member-func-from-literal //----------------------------------------------------------------------------- -void Chuck_Instr_Reg_Dup_Last_As_Pointer::execute( - Chuck_VM * vm, Chuck_VM_Shred * shred ) +static t_CKBYTE * special_primitive_alloc( const t_CKBYTE * copyFrom, t_CKUINT numBytes ) { - t_CKUINT *& reg_sp = (t_CKUINT *&)shred->reg->sp; - t_CKBYTE * where = (t_CKBYTE *)shred->reg->sp; - + t_CKBYTE * copy = new t_CKBYTE[numBytes]; + memcpy( copy, copyFrom, numBytes ); + return copy; +} +//----------------------------------------------------------------------------- +// reclaim memory in special-primitive storage; used for vec2/3/4 member function calls +// 1.5.4.2 (ge) added as part of #special-primitive-member-func-from-literal +//----------------------------------------------------------------------------- +static void special_primitive_cleanup( t_CKBYTE * reclaimMe ) +{ + CK_SAFE_DELETE_ARRAY( reclaimMe ); +} +//----------------------------------------------------------------------------- +// name: execute() | 1.5.4.2 (ge) added +// desc: assume a (primitive, e.g., vec2/3/4) value on reg stack, pop from stack, +// put into special-primitive storage, push its address to reg +// stack for a potential function call #special-primitive-member-func-from-literal +//----------------------------------------------------------------------------- +void Chuck_Instr_Reg_Transmute_Value_To_Pointer::execute( Chuck_VM * vm, Chuck_VM_Shred * shred ) +{ + // stack pointer + t_CKBYTE *& reg_sp = (t_CKBYTE *&)shred->reg->sp; + // pop the specified number of bytes + pop_( reg_sp, m_val ); + // allocate + t_CKBYTE * ptr = special_primitive_alloc( reg_sp, m_val ); + // get as uint pointer so the push_() pointer arithmetic works correctly + t_CKUINT *& the_sp = (t_CKUINT *&)reg_sp; // push pointer into reg stack - push_( reg_sp, (t_CKUINT)(where-(m_val*sz_WORD)) ); + push_( the_sp, (t_CKUINT)ptr ); } @@ -5327,9 +5352,10 @@ void Chuck_Instr_Func_Call::execute( Chuck_VM * vm, Chuck_VM_Shred * shred ) const char * Chuck_Instr_Func_Call_Member::params() const { static char buffer[CK_PRINT_BUF_LENGTH]; - snprintf( buffer, CK_PRINT_BUF_LENGTH, "%s, %s", + snprintf( buffer, CK_PRINT_BUF_LENGTH, "%s, %s, %s", m_func_ref ? m_func_ref->signature(FALSE,FALSE).c_str() : "[null]", - m_arg_convention == CK_FUNC_CALL_THIS_IN_BACK ? "this:back" : "this:front" ); + m_arg_convention == CK_FUNC_CALL_THIS_IN_BACK ? "this:back" : "this:front", + m_special_primitive_cleanup_this ? "transmute:yes" : "transmute:no" ); return buffer; } @@ -5345,6 +5371,8 @@ void Chuck_Instr_Func_Call_Member::execute( Chuck_VM * vm, Chuck_VM_Shred * shre t_CKUINT *& mem_sp = (t_CKUINT *&)shred->mem->sp; t_CKUINT *& reg_sp = (t_CKUINT *&)shred->reg->sp; Chuck_DL_Return retval; + // 1.5.4.2 (ge) added; #special-primitive-member-func-from-literal + t_CKUINT THIS = 0; // pop word pop_( reg_sp, 2 ); @@ -5399,6 +5427,9 @@ void Chuck_Instr_Func_Call_Member::execute( Chuck_VM * vm, Chuck_VM_Shred * shre *mem_sp2++ = *reg_sp2++; } + // remember THIS #special-primitive-member-func-from-literal + THIS = (t_CKUINT)(*mem_sp); + // check the function pointer kind: ctor or mfun? if( func->native_func_kind == ae_fp_ctor ) // ctor { @@ -5477,6 +5508,16 @@ void Chuck_Instr_Func_Call_Member::execute( Chuck_VM * vm, Chuck_VM_Shred * shre func_release_args( vm, m_func_ref->def()->arg_list, (t_CKBYTE *)(mem_sp+1) ); } + // check if we need to do special-primitive cleanup | 1.5.4.2 (ge) added + // this is for special primitives that have "member" functions + // e.g., vec2/3/4 -- specifically, this supports calling a "member" function + // from a literal value (i.e., not a variable) -- e.g., @(1,0).magnitude() + // NOTE: this approach is cursed because it adds more special case treatment + // to existing special cases for special primitives; if there is any silver + // lining, it might be that this logic is isolated and contained + // (see #special-primitive-member-func-from-literal for related code) + if( m_special_primitive_cleanup_this ) special_primitive_cleanup( (t_CKBYTE *)THIS ); + // pop the stack pointer mem_sp -= push; diff --git a/src/core/chuck_instr.h b/src/core/chuck_instr.h index 342779aa2..1b4ed0a01 100644 --- a/src/core/chuck_instr.h +++ b/src/core/chuck_instr.h @@ -2283,14 +2283,14 @@ struct Chuck_Instr_Reg_Dup_Last2 : public Chuck_Instr //----------------------------------------------------------------------------- -// name: struct Chuck_Instr_Reg_Dup_Last_As_Pointer +// name: struct Chuck_Instr_Reg_Transmute_Value_To_Pointer // desc: duplicate last value on stack as pointer; 1.3.5.3 //----------------------------------------------------------------------------- -struct Chuck_Instr_Reg_Dup_Last_As_Pointer : public Chuck_Instr_Unary_Op +struct Chuck_Instr_Reg_Transmute_Value_To_Pointer : public Chuck_Instr_Unary_Op { public: - Chuck_Instr_Reg_Dup_Last_As_Pointer( t_CKUINT sizeInWords ) - { this->set( sizeInWords ); } + Chuck_Instr_Reg_Transmute_Value_To_Pointer( t_CKUINT sizeInBytes ) + { this->set( sizeInBytes ); } virtual void execute( Chuck_VM * vm, Chuck_VM_Shred * shred ); }; @@ -3332,8 +3332,10 @@ struct Chuck_Instr_Func_Call_Member : public Chuck_Instr_Unary_Op { public: Chuck_Instr_Func_Call_Member( t_CKUINT ret_size, Chuck_Func * func_ref, - ck_Func_Call_Arg_Convention arg_convention = CK_FUNC_CALL_THIS_IN_BACK ) - { this->set( ret_size ); m_func_ref = func_ref; m_arg_convention = arg_convention; } + ck_Func_Call_Arg_Convention arg_convention = CK_FUNC_CALL_THIS_IN_BACK, + t_CKBOOL special_primitive_cleanup_this = FALSE ) + { this->set( ret_size ); m_func_ref = func_ref; m_arg_convention = arg_convention; + m_special_primitive_cleanup_this = special_primitive_cleanup_this; } public: // for carrying out instruction @@ -3342,11 +3344,14 @@ struct Chuck_Instr_Func_Call_Member : public Chuck_Instr_Unary_Op virtual const char * params() const; public: - // 1.5.0.0 (ge) | added for arg list cleanup + // 1.5.0.0 (ge) added for arg list cleanup Chuck_Func * m_func_ref; // when applicable, this flag indicates whether this/type is at the // beginning or at the end of the argument block on the reg stack ck_Func_Call_Arg_Convention m_arg_convention; + // 1.5.4.2 (ge) added only for special primitives that have "member" functions (vec2/3/4) + // #special-primitive-member-func-from-literal + t_CKBOOL m_special_primitive_cleanup_this; }; diff --git a/src/core/chuck_type.cpp b/src/core/chuck_type.cpp index a893fe6a7..3917892b1 100644 --- a/src/core/chuck_type.cpp +++ b/src/core/chuck_type.cpp @@ -4932,11 +4932,16 @@ t_CKTYPE type_engine_check_exp_dot_member_special( Chuck_Env * env, a_Exp_Dot_Me // check base; 1.3.5.3 if( member->base->s_meta == ae_meta_value ) // is literal { + // mark as special primitive member function call from literal + // 1.5.4.2 (ge) added #special-primitive-member-func-from-literal + member->isSpecialPrimitiveFunc = TRUE; + // error - EM_error2( member->base->where, - "cannot call function from literal %s value", - member->t_base->c_name() ); - return NULL; + // 1.5.4.2 (ge) commented out #special-primitive-member-func-from-literal + // EM_error2( member->base->where, + // "cannot call function from literal %s value", + // member->t_base->c_name() ); + // return NULL; } // find the value diff --git a/src/test/01-Basic/262-vec-literal-func.ck b/src/test/01-Basic/262-vec-literal-func.ck new file mode 100644 index 000000000..081ae9d23 --- /dev/null +++ b/src/test/01-Basic/262-vec-literal-func.ck @@ -0,0 +1,37 @@ +// (arcane!) unit test for special primitives calling "member" functions from a literal value +// created 1.5.4.2 | #special-primitive-member-func-from-literal + +fun void assert( float A, float B, int which ) +{ + if( !Math.equal( A, B ) ) + { + <<< "TEST #", which, "FAILED: expecting", A, "got", B >>>; + me.exit(); + } +} + +// some set up +@(1,2,3) => vec3 v1; +@(1,0,0) => vec3 v2a; +@(0,1,0) => vec3 v2b; +v2a + v2b => vec3 v2sum; + +// calling function from literal value +assert( @(1,2,3).magnitude(), v1.magnitude(), 1 ); +// calling from intermediate result (which is considered a literal value) +assert( (v2a+v2b).magnitude(), v2sum.magnitude(), 2 ); +// calling from intermediate results with literals +assert( (@(1,0,0)+@(0,1,0)).magnitude(), v2sum.magnitude(), 3 ); +// literals calling function with literal args +assert( @(1,2,3).dot(@(0,1,0)), 2, 4 ); +// various permuatations +assert( @(1,2,3).dot(v2b), 2, 5 ); +assert( v1.dot(@(0,1,0)), 2, 6 ); +assert( v1.dot(v2b), 2, 7 ); +// vec2 +assert( @(1,0).dot(@(3,0)), 3, 8 ); +// vec4 +assert( @(1,0,2,0).dot(@(3,0,1,0)), 5, 8 ); + +// if we got here then good +<<< "success" >>>; diff --git a/src/test/01-Basic/263-vec-literal-stack.ck b/src/test/01-Basic/263-vec-literal-stack.ck new file mode 100644 index 000000000..822cf4d2d --- /dev/null +++ b/src/test/01-Basic/263-vec-literal-stack.ck @@ -0,0 +1,24 @@ +// testing stack alignment + +// how many iterations +250000 => int N; + +// some set up +@(1,2,3) => vec3 v1; +@(1,0,0) => vec3 v2a; +@(0,1,0) => vec3 v2b; +v2a + v2b => vec3 v2sum; + +// calling function from literal value +repeat(N) @(1,2,3).magnitude(); +repeat(N) (v2a+v2b).magnitude(); +repeat(N) (@(1,0,0)+@(0,1,0)).magnitude(); +repeat(N) @(1,2,3).dot(@(0,1,0)); +repeat(N) @(1,2,3).dot(v2b); +repeat(N) v1.dot(@(0,1,0)); +repeat(N) v1.dot(v2b); +repeat(N) @(1,0).dot(@(3,0)); +repeat(N) @(1,0,2,0).dot(@(3,0,1,0)); + +// if we got here then good +<<< "success" >>>;