from stdio import printf import testcheck # Custom decorator handler calling convention (v4): i32 decor(ctx, func_name, phase, args_ptr, ret_ptr, decor_args_ptr). # ctx: per-call frame-local context (32 bytes); args_ptr/ret_ptr are read-write; pre-phase returns 0=skip, 1=call once, N=loop N. # @log: print enter/exit, return 1 (call once) export def log(ctx: rawptr, func_name: str, phase: int, args_ptr: rawptr, ret_ptr: rawptr, decor_args_ptr: rawptr) -> int: if phase == 0: printf("[LOG] >> %s enter\n", func_name) return 1 else: printf("[LOG] << %s exit\n", func_name) return 0 # @trace: verbose tracing export def trace(ctx: rawptr, func_name: str, phase: int, args_ptr: rawptr, ret_ptr: rawptr, decor_args_ptr: rawptr) -> int: if phase == 0: printf("[TRACE] calling %s...\n", func_name) return 1 else: printf("[TRACE] %s returned\n", func_name) return 0 # @timing: measures elapsed time using a global counter. # A global is used because i8* to i32* pointer casts are not supported yet. _timing_tmp: int = 0 export def timing(ctx: rawptr, func_name: str, phase: int, args_ptr: rawptr, ret_ptr: rawptr, decor_args_ptr: rawptr) -> int: global _timing_tmp if phase == 0: _timing_tmp = 100 printf("[TIMING] %s start at t=%d\n", func_name, _timing_tmp) return 1 else: elapsed: int = 105 - _timing_tmp printf("[TIMING] %s took %d ms\n", func_name, elapsed) return 0 # @repeat (with args, flow hijack): call the original function N times export def repeat(ctx: rawptr, func_name: str, phase: int, args_ptr: rawptr, ret_ptr: rawptr, decor_args_ptr: rawptr) -> int: if phase == 0: printf("[REPEAT] %s will run 3 times\n", func_name) return 3 else: printf("[REPEAT] %s all iterations done\n", func_name) return 0 # @skip: skip the original call (flow hijack, returns 0) _skip_count: int = 0 export def skip(ctx: rawptr, func_name: str, phase: int, args_ptr: rawptr, ret_ptr: rawptr, decor_args_ptr: rawptr) -> int: global _skip_count if phase == 0: _skip_count = _skip_count + 1 printf("[SKIP] %s skipped! (call #%d)\n", func_name, _skip_count) return 0 else: printf("[SKIP] %s post (ret_ptr=%p)\n", func_name, ret_ptr) return 0 # @count_calls: count invocations (used to test recursion guard) _call_count: int = 0 export def count_calls(ctx: rawptr, func_name: str, phase: int, args_ptr: rawptr, ret_ptr: rawptr, decor_args_ptr: rawptr) -> int: global _call_count if phase == 0: _call_count = _call_count + 1 printf("[COUNT] %s call #%d\n", func_name, _call_count) return 1 else: printf("[COUNT] %s done (total calls: %d)\n", func_name, _call_count) return 0 # --- Test 1: single @log --- @log export def add(a: int, b: int) -> int: return a + b # --- Test 2: chained @log @trace (log(trace(f))) --- @log @trace export def multiply(a: int, b: int) -> int: return a * b # --- Test 3: @log on void function --- @log export def greet(name: str): printf("Hello, %s!\n", name) # --- Test 4: @log on no-arg function --- @log export def get_value() -> int: return 42 # --- Test 5: @timing --- @timing export def compute(x: int) -> int: return x * x + 1 # --- Test 6: @repeat(3) flow hijack --- @repeat(3) export def echo(msg: str): printf(" echo: %s\n", msg) # --- Test 7: chained @timing @log --- @timing @log export def power(base: int, exp: int) -> int: result: int = 1 for i in range(exp): result = result * base return result # --- Test 8: @skip flow hijack --- @skip export def dangerous() -> int: printf("This should NOT be printed!\n") return 999 # --- Test 9: @repeat(3) with return value --- @repeat(3) export def accumulate(x: int) -> int: printf(" accumulate(%d)\n", x) return x * 10 # --- Test 10: recursion guard, @count_calls on recursive fn --- @count_calls export def factorial(n: int) -> int: if n <= 1: return 1 return n * factorial(n - 1) # --- Test 11: @log on recursive fib --- @log export def fib(n: int) -> int: if n <= 1: return n return fib(n - 1) + fib(n - 2) # --- main --- export def main() -> int: testcheck.begin("Decorator Test v4") # Test 1: single @log testcheck.section("Test 1: single @log") r1: int = add(3, 4) printf("add(3,4) = %d\n", r1) testcheck.check(r1 == 7, "add(3,4) = 7", "add(3,4) expect 7") # Test 2: chained @log @trace testcheck.section("Test 2: chained @log @trace") r2: int = multiply(5, 6) printf("multiply(5,6) = %d\n", r2) testcheck.check(r2 == 30, "multiply(5,6) = 30", "multiply(5,6) expect 30") # Test 3: void return testcheck.section("Test 3: @log on void function") greet("Viper") testcheck.ok("@log on void function greet") # Test 4: no arguments testcheck.section("Test 4: @log on no-arg function") r4: int = get_value() printf("get_value() = %d\n", r4) testcheck.check(r4 == 42, "get_value() = 42", "get_value() expect 42") # Test 5: @timing testcheck.section("Test 5: @timing") r5: int = compute(7) printf("compute(7) = %d\n", r5) testcheck.check(r5 == 50, "compute(7) = 50", "compute(7) expect 50") # Test 6: @repeat(3) flow hijack, loops 3 times testcheck.section("Test 6: @repeat(3) flow hijack") echo("hello") testcheck.ok("@repeat(3) flow hijack echo") # Test 7: chained @timing @log testcheck.section("Test 7: chained @timing @log") r7: int = power(2, 10) printf("power(2,10) = %d\n", r7) testcheck.check(r7 == 1024, "power(2,10) = 1024", "power(2,10) expect 1024") # Test 8: @skip, skips the original call testcheck.section("Test 8: @skip flow hijack") r8: int = dangerous() printf("dangerous() = %d (should be 0, skipped)\n", r8) testcheck.check(r8 == 0, "dangerous() skipped = 0", "dangerous() expect 0") # Test 9: @repeat(3) with return value testcheck.section("Test 9: @repeat(3) with return value") r9: int = accumulate(5) printf("accumulate(5) = %d (last iteration result)\n", r9) testcheck.check(r9 == 50, "accumulate(5) = 50", "accumulate(5) expect 50") # Test 10: recursion guard, @count_calls testcheck.section("Test 10: recursive decoration protection") global _call_count _call_count = 0 r10: int = factorial(5) printf("factorial(5) = %d (should be 120)\n", r10) printf(" decorator call count = %d (should be 1, not 5)\n", _call_count) testcheck.check(r10 == 120 and _call_count == 1, "factorial(5)=120, calls=1", "factorial(5) expect 120, calls=1") # Test 11: @log recursion guard, fib testcheck.section("Test 11: @log on recursive fib") r11: int = fib(6) printf("fib(6) = %d (should be 8)\n", r11) testcheck.check(r11 == 8, "fib(6) = 8", "fib(6) expect 8") return testcheck.end()