#Parse Puzzle Input

This guide shows common parsing patterns for structured text input: one number per line, comma-separated values, blank-line sections, coordinates, key/value fields, and mixed text. These are the kinds of transformations that come up constantly in Advent of Code problems.

#Read a whole file and trim it

Use .read() when the input is one block of text, then .strip() to remove the trailing newline:

fn main() {
    let raw = File("input.txt").read().strip();
    Println(raw);
}

This is useful for single-line inputs like 389125467 or target area: x=20..30, y=-10..-5.

#Parse one integer per line

When every line is a number, convert the full array in one step:

fn main() {
    let nums = File("input.txt").lines().to_int();
    Println(nums.sum());
}

This is the shortest pattern for inputs like:

199
200
208
210

#Parse comma-separated integers

For inputs stored on a single line, read the line, split on commas, then convert:

fn main() {
    let line = File("input.txt").lines()[0];
    let nums = line.split(",").to_int();
    Println(nums);
}

If you already have the full file as a string:

let nums = File("input.txt").read().strip().split(",").to_int();

#Parse blank-line sections

Use .sections() when the file is divided into groups separated by blank lines:

fn main() {
    let groups = File("input.txt").sections();

    for group in groups {
        let nums = group.split("\n").to_int();
        Println(nums.sum());
    }
}

This pattern is useful for grouped calorie counts, multiple decks, batches of rules, or map tiles.

#Split each row into fields

Use .split() with no argument for whitespace-delimited data:

fn main() {
    let rows = File("input.txt").lines();

    for row in rows {
        let fields = row.split();
        Println(fields);
    }
}

Convert the fields immediately when they should all be numbers:

fn main() {
    let rows = File("input.txt").lines();

    for row in rows {
        let nums = row.split().to_int();
        Println(nums.max() - nums.min());
    }
}

#Use split_once() for two-part records

When each line has exactly one delimiter, split_once() is usually clearer than split():

let (cmd, value) = "forward 10".split_once(" ");
let n = Int(value);

For coordinate pairs:

obj Point {
    x: Int;
    y: Int;
}

fn parse_point(raw: Str) -> Point {
    let (raw_x, raw_y) = raw.split_once(",");
    return Point(x=Int(raw_x), y=Int(raw_y));
}

fn parse_segment(line: Str) -> Tuple(Point, Point) {
    let (left, right) = line.split_once(" -> ");
    return Tuple(parse_point(left), parse_point(right));
}

This is a good fit for inputs like 498,4 -> 498,6 or Player 1: 4.

#Extract numbers from mixed text

For lines with fixed prefixes, peel the text away in stages:

fn main() {
    let line = "target area: x=20..30, y=-10..-5";

    let parts = line.strip_prefix("target area: ").split(", ");
    let xparts = parts[0].strip_prefix("x=").split("..").to_int();
    let yparts = parts[1].strip_prefix("y=").split("..").to_int();

    let xmin = xparts[0];
    let xmax = xparts[1];
    let ymin = yparts[0];
    let ymax = yparts[1];

    Println("{xmin} {xmax} {ymin} {ymax}");
}

This approach works well when the format is regular and the separators are stable.

#Parse key/value records

Some inputs encode records as whitespace-separated key:value pairs:

fn main() {
    let records = File("input.txt").sections();

    for record in records {
        var fields = Hashmap(Str, Str);

        for pair in record.split() {
            let (key, value) = pair.split_once(":");
            fields[key] = value;
        }

        Println(fields["byr"]);
    }
}

Using .sections() here is usually better than .lines() because one record can span multiple lines.

#Convert individual values to the right type

Use the built-in conversion functions when fields have different types:

fn main() {
    let fields = "42 3.5".split();

    let count = Int(fields[0]);
    let ratio = Float(fields[1]);

    Println("{count} {ratio}");
}

For whole arrays, the array conversions are shorter:

let ints = ["1", "2", "3"].to_int();
let floats = ["1.5", "2.5"].to_float();

#Build small helpers for repeated parsing

As soon as a format repeats, move the parsing into a function:

fn parse_rule(line: Str) -> Tuple(Str, Int, Int) {
    let (name, rest) = line.split_once(": ");
    let ranges = rest.split(" or ");
    let left = ranges[0].split("-").to_int();
    return Tuple(name, left[0], left[1]);
}

Small parsing helpers keep the main solving code focused on the algorithm instead of string manipulation.

#A complete AoC-style example

This combines file reading, section splitting, and integer conversion:

fn main() {
    let groups = File("input.txt").sections();

    var sums: Array(Int) = [];
    for group in groups {
        let nums = group.split("\n").to_int();
        sums.add(nums.sum());
    }

    sums = sums.sorted("desc");
    Println("Top group: {sums[0]}");
    Println("Top three: {sums[0] + sums[1] + sums[2]}");
}

#See also