# A Command-Line Parser

### A Command-Line Parser

Many programs begin the same way: they read command-line arguments, decide what the user requested, then execute an operation.

A command-line parser converts raw argument strings into structured program state.

Suppose we want a small utility named `calc`. It accepts two subcommands:

```text
calc add 10 20
calc mul 4 5
```

The first version can be written directly.

```zig
const std = @import("std");

pub fn main() !void {
    var args = std.process.args();

    _ = args.next();

    const command = args.next() orelse {
        std.debug.print("missing command\n", .{});
        return;
    };

    const a_text = args.next() orelse {
        std.debug.print("missing first number\n", .{});
        return;
    };

    const b_text = args.next() orelse {
        std.debug.print("missing second number\n", .{});
        return;
    };

    const a = try std.fmt.parseInt(i32, a_text, 10);
    const b = try std.fmt.parseInt(i32, b_text, 10);

    if (std.mem.eql(u8, command, "add")) {
        std.debug.print("{d}\n", .{a + b});
    } else if (std.mem.eql(u8, command, "mul")) {
        std.debug.print("{d}\n", .{a * b});
    } else {
        std.debug.print("unknown command: {s}\n", .{command});
    }
}
```

Run it:

```sh
zig run main.zig -- add 10 20
```

The output is:

```text
30
```

The `--` separates Zig's own options from the program's arguments.

The function:

```zig
std.process.args()
```

returns an iterator over command-line arguments.

The first argument is normally the executable path. This program ignores it:

```zig
_ = args.next();
```

The underscore assignment means: evaluate the value, but discard it.

Arguments are read one at a time:

```zig
const command = args.next() orelse {
    ...
};
```

`args.next()` returns an optional slice:

```zig
?[]const u8
```

If no argument exists, the value is `null`.

The `orelse` expression handles the null case immediately.

Strings in Zig are byte slices:

```zig
[]const u8
```

To compare strings, Zig uses library functions:

```zig
std.mem.eql(u8, command, "add")
```

This compares two slices byte-by-byte.

The arguments `"10"` and `"20"` are text. They must be converted into integers:

```zig
const a = try std.fmt.parseInt(i32, a_text, 10);
```

The arguments are:

| Argument | Meaning |
|---|---|
| `i32` | destination type |
| `a_text` | source text |
| `10` | numeric base |

If parsing fails, `parseInt` returns an error. `try` propagates the error to the caller.

The structure of this program is common in Zig:

1. Read raw input.
2. Validate it early.
3. Convert text into typed values.
4. Dispatch using explicit control flow.
5. Report errors directly.

As the number of commands grows, nested `if` expressions become awkward.

An enum gives the commands names.

```zig
const Command = enum {
    add,
    mul,
};
```

Now parsing can be separated from execution.

```zig
const std = @import("std");

const Command = enum {
    add,
    mul,
};

fn parseCommand(text: []const u8) !Command {
    if (std.mem.eql(u8, text, "add")) {
        return .add;
    }

    if (std.mem.eql(u8, text, "mul")) {
        return .mul;
    }

    return error.UnknownCommand;
}

pub fn main() !void {
    var args = std.process.args();

    _ = args.next();

    const command_text = args.next() orelse {
        return error.MissingCommand;
    };

    const a_text = args.next() orelse {
        return error.MissingArgument;
    };

    const b_text = args.next() orelse {
        return error.MissingArgument;
    };

    const command = try parseCommand(command_text);

    const a = try std.fmt.parseInt(i32, a_text, 10);
    const b = try std.fmt.parseInt(i32, b_text, 10);

    switch (command) {
        .add => {
            std.debug.print("{d}\n", .{a + b});
        },
        .mul => {
            std.debug.print("{d}\n", .{a * b});
        },
    }
}
```

This version divides the program into smaller operations:

| Function | Responsibility |
|---|---|
| `parseCommand` | convert text into an enum |
| `main` | coordinate program flow |
| `parseInt` | convert numbers |
| `switch` | dispatch execution |

This style scales well.

A larger program may add:

- flags such as `--verbose`
- optional arguments
- configuration files
- subcommands
- environment variables

The core structure remains the same: parse first, execute later.

Exercise 20-1. Add a `sub` command.

Exercise 20-2. Print a usage message when arguments are missing.

Exercise 20-3. Add support for hexadecimal input.

Exercise 20-4. Add a `--help` flag.

Exercise 20-5. Move argument parsing into a separate file.

