✓
Passing This code compiles and runs correctly.
Code
// Test: Interpreter for loop
//
// Tests that ~for(0..N) | each i |> body | done |> ... works
~import std/runtime
~import std/interpreter
~import std/io
const std = @import("std");
// Event that prints a number
~pub event print-num { n: []const u8 }
~proc print-num|zig {
std.debug.print("[print_num] n = {s}\n", .{n});
}
// Register events including control flow
~std/runtime:register(scope: "test") {
for
print-num
}
// Test flow: loop 3 times, print each iteration
const TEST_FLOW =
\\~for(0..3)
\\| each i |> print_num(n: i)
\\| done |> result { status: "complete" }
;
~std/interpreter:run(source: TEST_FLOW, dispatcher: dispatch_test)
| result r |> std/io:print.ln("Result: {{ r.value.branch:s }}")
| exhausted e |> std/io:print.ln("Exhausted: {{ e.last_event:s }}")
| parse-error e |> std/io:print.ln("Parse error: {{ e.message:s }}")
| validation-error e |> std/io:print.ln("Validation error: {{ e:s }}")
| dispatch-error e |> std/io:print.ln("Dispatch error: {{ e.message:s }}")
Actual
Dispatch error: EventDenied
Imported Files
// HONEST INTERPRETER BENCHMARK
//
// Task: Sum numbers 0..99 using for loop
// Measure: Parse once, execute N times, report per-execution time
// Compare: Python doing the same thing
~import std/runtime
~import std/interpreter
const std = @import("std");
// ============================================================================
// Events for computation
// ============================================================================
~pub event add { a: []const u8, b: []const u8 }
| sum []const u8
~proc add|zig {
const a_int = std.fmt.parseInt(i64, a, 10) catch 0;
const b_int = std.fmt.parseInt(i64, b, 10) catch 0;
const result_int = a_int + b_int;
var buf: [32]u8 = undefined;
const result_str = std.fmt.bufPrint(&buf, "{d}", .{result_int}) catch "0";
return .{ .sum = result_str };
}
~pub event init-sum {}
| value []const u8
~proc init-sum|zig {
return .{ .value = "0" };
}
// Register events including for
~std/runtime:register(scope: "bench") {
for
add
init-sum
}
// The benchmark flow: sum 0..100
const BENCH_FLOW =
\\~for(0..100)
\\| each i |> add(a: "0", b: i)
\\| done |> result { status: "complete" }
;
// Simple flow for comparison (no loop)
const SIMPLE_FLOW =
\\~add(a: "21", b: "21")
;
// ============================================================================
// BENCHMARK
// ============================================================================
pub fn main() void {
const koru_parser = @import("koru_parser");
std.debug.print("\n", .{});
std.debug.print("============================================================\n", .{});
std.debug.print("HONEST INTERPRETER BENCHMARK\n", .{});
std.debug.print("============================================================\n\n", .{});
const ITERATIONS = 1000;
// ========================================================================
// BENCHMARK 1: Simple dispatch (no loop) - parse once, dispatch N times
// ========================================================================
std.debug.print("[1] SIMPLE DISPATCH: ~add(a: \"21\", b: \"21\")\n", .{});
std.debug.print(" Iterations: {d}\n", .{ITERATIONS});
{
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
// Parse once
var parser = koru_parser.Parser.init(allocator, SIMPLE_FLOW, "bench", &[_][]const u8{}, null) catch {
std.debug.print(" ERROR: Parser init failed\n", .{});
return;
};
defer parser.deinit();
const parse_result = parser.parse() catch {
std.debug.print(" ERROR: Parse failed\n", .{});
return;
};
// Find the flow
var inv_ptr: ?*const @import("ast").Invocation = null;
for (parse_result.source_file.items) |*item| {
if (item.* == .flow) {
inv_ptr = item.flow.inv();
break;
}
}
if (inv_ptr == null) {
std.debug.print(" ERROR: No flow found\n", .{});
return;
}
// Warm up
for (0..10) |_| {
var result: koru_std.runtime.DispatchResult = undefined;
dispatch_bench(inv_ptr.?, &result) catch continue;
}
// Time it
const start = std.time.nanoTimestamp();
for (0..ITERATIONS) |_| {
var result: koru_std.runtime.DispatchResult = undefined;
dispatch_bench(inv_ptr.?, &result) catch continue;
}
const end = std.time.nanoTimestamp();
const total_ns = end - start;
const per_iter_ns = @divTrunc(total_ns, ITERATIONS);
std.debug.print(" Total: {d} ms\n", .{@divTrunc(total_ns, 1_000_000)});
std.debug.print(" Per dispatch: {d} ns\n", .{per_iter_ns});
}
std.debug.print("\n", .{});
// ========================================================================
// BENCHMARK 2: Full interpreter with for loop
// ========================================================================
std.debug.print("[2] FULL INTERPRETER: ~for(0..100) with adds\n", .{});
std.debug.print(" Iterations: {d}\n", .{ITERATIONS});
{
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
// Parse once
var parser = koru_parser.Parser.init(allocator, BENCH_FLOW, "bench", &[_][]const u8{}, null) catch {
std.debug.print(" ERROR: Parser init failed\n", .{});
return;
};
defer parser.deinit();
const parse_result = parser.parse() catch {
std.debug.print(" ERROR: Parse failed\n", .{});
return;
};
// Find the flow
var flow_ptr: ?*const @import("ast").Flow = null;
for (parse_result.source_file.items) |*item| {
if (item.* == .flow) {
flow_ptr = &item.flow;
break;
}
}
if (flow_ptr == null) {
std.debug.print(" ERROR: No flow found\n", .{});
return;
}
// Warm up (run a few iterations) using reusable env/bindings
var env = koru_std.interpreter.Environment.init(allocator);
defer env.deinit();
var expr_bindings = koru_std.interpreter.ExprBindings.init(allocator);
defer expr_bindings.deinit();
for (0..3) |_| {
env.clear();
expr_bindings.clear();
var ctx = koru_std.interpreter.InterpreterContext{
.env = &env,
.expr_bindings = &expr_bindings,
.allocator = allocator,
.dispatcher = &dispatch_bench,
};
_ = koru_std.interpreter.executeFlow(flow_ptr.?, &ctx) catch continue;
}
// Time it
const start = std.time.nanoTimestamp();
for (0..ITERATIONS) |_| {
env.clear();
expr_bindings.clear();
var ctx = koru_std.interpreter.InterpreterContext{
.env = &env,
.expr_bindings = &expr_bindings,
.allocator = allocator,
.dispatcher = &dispatch_bench,
};
_ = koru_std.interpreter.executeFlow(flow_ptr.?, &ctx) catch continue;
}
const end = std.time.nanoTimestamp();
const total_ns = end - start;
const per_iter_ns = @divTrunc(total_ns, ITERATIONS);
const per_iter_us = @divTrunc(per_iter_ns, 1000);
std.debug.print(" Total: {d} ms\n", .{@divTrunc(total_ns, 1_000_000)});
std.debug.print(" Per execution: {d} us ({d} ns)\n", .{per_iter_us, per_iter_ns});
std.debug.print(" (Each execution = 100 loop iterations + 100 add dispatches)\n", .{});
}
// ========================================================================
// BENCHMARK 3: Parse time only
// ========================================================================
std.debug.print("[3] PARSE ONLY: ~for(0..100) flow\n", .{});
std.debug.print(" Iterations: {d}\n", .{ITERATIONS});
{
// Time parsing only
const start = std.time.nanoTimestamp();
for (0..ITERATIONS) |_| {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
var parser = koru_parser.Parser.init(arena.allocator(), BENCH_FLOW, "bench", &[_][]const u8{}, null) catch continue;
defer parser.deinit();
_ = parser.parse() catch continue;
}
const end = std.time.nanoTimestamp();
const total_ns = end - start;
const per_iter_ns = @divTrunc(total_ns, ITERATIONS);
const per_iter_us = @divTrunc(per_iter_ns, 1000);
std.debug.print(" Total: {d} ms\n", .{@divTrunc(total_ns, 1_000_000)});
std.debug.print(" Per parse: {d} us ({d} ns)\n", .{per_iter_us, per_iter_ns});
}
std.debug.print("\n", .{});
std.debug.print("============================================================\n", .{});
std.debug.print("COMPARE WITH: python3 benchmark.py\n", .{});
std.debug.print("============================================================\n", .{});
}
Test Configuration
MUST_RUN