013 interpreter test

✓ Passing This code compiles and runs correctly.

Code

// Test: Koru Interpreter
// Tests the interpreter parsing and executing Koru code at runtime

~import "$std/runtime"
~import "$std/interpreter"


const std = @import("std");

// ============================================================================
// Events to dispatch to
// ============================================================================

~pub event greet { name: []const u8 }
| greeted { message: []const u8 }

~proc greet {
    std.debug.print("GREET handler: Hello, {s}!\n", .{name});
    return .{ .greeted = .{ .message = "Hello!" } };
}

~pub event farewell { name: []const u8 }
| departed { message: []const u8 }

~proc farewell {
    std.debug.print("FAREWELL handler: Goodbye, {s}!\n", .{name});
    return .{ .departed = .{ .message = "Goodbye!" } };
}

// Register events for runtime dispatch
~std.runtime:register(scope: "test") {
    greet
    farewell
}

// ============================================================================
// Entry point - tests the interpreter
// ============================================================================

pub fn main() void {
    std.debug.print("=== INTERPRETER TEST ===\n\n", .{});

    // Get imports
    const koru_parser = @import("koru_parser");
    const koru_errors = @import("koru_errors");

    // Use the generated dispatcher directly
    const dispatcher_fn = dispatch_test;

    // ========================================================================
    // Test 1: Manual dispatch (baseline)
    // ========================================================================
    std.debug.print("Test 1: Manual dispatch 'greet'\n", .{});
    {
        // Use inline types from the generated registry
        const segments = [_][]const u8{"greet"};
        const path = Path{
            .module_qualifier = null,
            .segments = &segments,
        };

        var args_array = [_]Arg{
            .{ .name = "name", .value = "World" },
        };

        const inv = Invocation{
            .path = path,
            .args = &args_array,
        };

        var result: DispatchResult = undefined;
        dispatcher_fn(&inv, &result) catch |err| {
            std.debug.print("ERROR: {}\n", .{err});
            return;
        };

        std.debug.print("Result: branch={s}\n", .{result.branch});
        std.debug.print("=== TEST 1 PASSED ===\n\n", .{});
    }

    // ========================================================================
    // Test 2: Parse Koru source and dispatch!
    // ========================================================================
    std.debug.print("Test 2: Parse and dispatch '~greet(name: \"Runtime\")'\n", .{});
    {
        const test_source = "~greet(name: \"Runtime\")";

        // Create allocator
        var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
        defer arena.deinit();
        const allocator = arena.allocator();

        // Create error reporter
        var reporter = koru_errors.ErrorReporter.init(
            allocator,
            "test",
            test_source
        ) catch {
            std.debug.print("ERROR: Failed to init reporter\n", .{});
            return;
        };
        defer reporter.deinit();

        // Initialize parser
        var parser = koru_parser.Parser.init(
            allocator,
            test_source,
            "runtime_input",
            &[_][]const u8{},
            null
        ) catch {
            std.debug.print("ERROR: Failed to init parser\n", .{});
            return;
        };
        defer parser.deinit();

        // Parse!
        const parse_result = parser.parse() catch {
            std.debug.print("ERROR: Parse failed\n", .{});
            if (parser.reporter.hasErrors()) {
                const err = parser.reporter.errors.items[0];
                std.debug.print("  {s} at {}:{}\n", .{err.message, err.location.line, err.location.column});
            }
            return;
        };

        std.debug.print("Parsed {d} items\n", .{parse_result.source_file.items.len});

        // Find the flow
        var flow_found = false;
        for (parse_result.source_file.items) |item| {
            if (item == .flow) {
                flow_found = true;
                const flow = item.flow;
                const ast_inv = &flow.invocation;

                std.debug.print("Found flow: {s} with {d} args\n", .{
                    if (ast_inv.path.segments.len > 0) ast_inv.path.segments[0] else "?",
                    ast_inv.args.len
                });

                // Cast ast.Invocation to inline Invocation (same memory layout)
                const inv: *const Invocation = @ptrCast(ast_inv);

                // Dispatch it!
                var result: DispatchResult = undefined;
                dispatcher_fn(inv, &result) catch |err| {
                    std.debug.print("ERROR: Dispatch failed: {}\n", .{err});
                    return;
                };

                std.debug.print("Result: branch={s}\n", .{result.branch});
                break;
            }
        }

        if (!flow_found) {
            std.debug.print("ERROR: No flow found in parsed source\n", .{});
            return;
        }

        std.debug.print("=== TEST 2 PASSED ===\n\n", .{});
    }

    // ========================================================================
    // Test 3: Parse multi-arg call
    // ========================================================================
    std.debug.print("Test 3: Parse '~farewell(name: \"Claude\")'\n", .{});
    {
        const test_source = "~farewell(name: \"Claude\")";

        var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
        defer arena.deinit();
        const allocator = arena.allocator();

        var reporter = koru_errors.ErrorReporter.init(allocator, "test", test_source) catch return;
        defer reporter.deinit();

        var parser = koru_parser.Parser.init(allocator, test_source, "runtime", &[_][]const u8{}, null) catch return;
        defer parser.deinit();

        const parse_result = parser.parse() catch {
            std.debug.print("ERROR: Parse failed\n", .{});
            return;
        };

        for (parse_result.source_file.items) |item| {
            if (item == .flow) {
                const ast_inv = &item.flow.invocation;
                const inv: *const Invocation = @ptrCast(ast_inv);

                var result: DispatchResult = undefined;
                dispatcher_fn(inv, &result) catch |err| {
                    std.debug.print("ERROR: {}\n", .{err});
                    return;
                };

                std.debug.print("Result: branch={s}\n", .{result.branch});
                break;
            }
        }

        std.debug.print("=== TEST 3 PASSED ===\n\n", .{});
    }

    std.debug.print("=== ALL INTERPRETER TESTS PASSED ===\n", .{});
}
input.kz