summaryrefslogtreecommitdiff
path: root/exercises/074_comptime9.zig
blob: 50894fd0994d78c9a572333dbe1bc20e584ff80c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
const print = @import("std").debug.print;

// We're going to (ab)use the power of Zig to make animal hybrid creatures!
// What do you think a GatorMouse would look like?  Eek.
//
// Let's try a MouseLlama instead.
//
// We'll make a function that runs at comptime and takes a short code describing
// the desired creature. A Mouse is represented by "m" and a Llama is "lm".
// A MouseLlama hybrid, then, would be represented by "mlm".

const Animal = enum {
    Mouse,
    Llama,
    Gator,
};

// makeCreature takes the count of animals making up the hybrid creature (so we
// know how big a pen we'll need) and a format string, like the "mlm" for
// MouseLlama.
fn makeCreature(comptime count: usize, comptime fmt: []const u8) [count]Animal {

    // Since not every animal is represented by a single character, we need to
    // track the state of things as we move along. For example, if we see an
    // "m", is that a new Mouse or the end of a Llama?
    const State = enum {
        start, // Ready to start a new animal.
        l, // This means we've seen an "l", so if we see an "m", we know it's a Llama.
    };
    var state = State.start;

    // We return an array of animals representing the creature. (This is why we
    // really needed the 'count' parameter. Arrays need a size.)
    var animals: [count]Animal = .{undefined} ** count;
    var next_animal: usize = 0;

    inline for (fmt) |char| {

        // This is a good spot to add a @compileLog() call if you need to debug
        // any variables... (Come back here after you see main().)

        switch (state) {
            .start => switch (char) {
                // We've seen the start of a Llama.
                'l' => state = .l,

                // Mice are smaller.  An "m" is a full Mouse.
                'm' => {
                    animals[next_animal] = .Mouse;
                    next_animal += 1;
                },

                // @compileError lets us stop the build immediately if something
                // is wrong. It's like @compileLog but it prints a message
                // instead of inspecting values.
                //
                // What do you think happens with Gators? Do they join with
                // other animals or is this an error?
                'g' => ???,

                else => @compileError("No animal starts with '" ++ char ++ "'!"),
            },

            .l => switch (char) {
                // We've seen the end of a Llama.
                'm' => {
                    animals[next_animal] = .Llama;
                    next_animal += 1;
                    // Something is missing here. After we finish a Llama, we
                    // need to be ready to _start_ over with a new animal...
                    ???
                },

                else => @compileError("Only llamas start with 'l'!"),
            },
        }
    }

    if (state != .start) {
        @compileError("Oh no, an incomplete llama!");
    }
    if (next_animal != count) {
        @compileError("Creature is missing an animal (format string too short).");
    }

    return animals;
}

pub fn main() void {
    // Once you've fixed the ??? marks above, this makeCreature call will still
    // only succeed if you move it outside of main, so it will run at comptime.
    //
    // With the call here, Zig will try to make the creature at runtime, and
    // you'll get an interesting error.
    //
    // You may think the state got mixed up, but if you use @compileLog to check
    // some variables in makeCreature, you'll see that Zig is trying to compare
    // comptime values with "[runtime value]", which will never match.
    //
    // You can solve this by adding "comptime" to two of the variables in
    // makeCreature...
    const creature = makeCreature(2, "mlm");

    for (creature) |animal| {
        // @tagName gives us a string representing which variant of an enum we
        // have. This lets us print the names of animals without repeating them
        // here.
        print("{s}", .{@tagName(animal)});
    }
    print(" joins the crew!", .{});
}