diff --git a/README.md b/README.md index 6aec3f7..f6e2acd 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ opam install . --deps-only --with-test ## Run ``` -dune exec aoc -- --help -dune exec aoc -- --day 1 --part 1 -dune exec aoc -- -d1 -p1 +dune exec aoc -- -help +dune exec aoc -- -day 1 -part 1 +dune exec aoc -- -d 1 -p 1 ``` ## Build diff --git a/aoc.opam b/aoc.opam index 8c22df5..89630ac 100644 --- a/aoc.opam +++ b/aoc.opam @@ -11,7 +11,7 @@ depends: [ "dune" {>= "3.10"} "angstrom" "core" - "cmdliner" + "core_unix" "ppx_jane" "re" "odoc" {with-doc} diff --git a/aoc.opam.locked b/aoc.opam.locked index 899b64f..90d795c 100644 --- a/aoc.opam.locked +++ b/aoc.opam.locked @@ -9,8 +9,8 @@ homepage: "https://github.com/drewolson/aoc-ocaml" bug-reports: "https://github.com/drewolson/aoc-ocaml/issues" depends: [ "angstrom" {= "0.15.0"} - "cmdliner" {= "1.2.0"} "core" {= "v0.16.2"} + "core_unix" {= "v0.16.0"} "dune" {= "3.11.1"} "ocaml" {= "5.1.0"} "ppx_jane" {= "v0.16.0"} diff --git a/bin/dune b/bin/dune index e0e8e49..709e708 100644 --- a/bin/dune +++ b/bin/dune @@ -3,7 +3,10 @@ (name main) (libraries core - cmdliner + core_unix.command_unix runner) + (preprocess + (pps + ppx_jane)) (flags -open Core)) diff --git a/bin/main.ml b/bin/main.ml index be2799d..cbbbcbb 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -1,36 +1,39 @@ -module Arg = Cmdliner.Arg -module Cmd = Cmdliner.Cmd -module Term = Cmdliner.Term - -let ( & ), ( $ ) = Arg.(( & )), Cmdliner.Term.(( $ )) - -let year = - let doc = "Year to run" in - Arg.value & Arg.opt Arg.int 2022 & Arg.info [ "y"; "year" ] ~docv:"YEAR" ~doc +let year_arg = + Command.Arg_type.create (fun year_str -> + match Int.of_string_opt year_str with + | Some year when year = 2022 || year = 2023 -> year + | _ -> failwith "Year must be [2022, 2023]") ;; -let day = - let doc = "Day to run (1 - 25)" in - let days = List.map ~f:(fun d -> Int.to_string d, d) (List.init 25 ~f:Int.succ) in - Arg.required - & Arg.opt (Arg.some & Arg.enum days) None - & Arg.info [ "d"; "day" ] ~docv:"DAY" ~doc +let day_arg = + Command.Arg_type.create (fun day_str -> + match Int.of_string_opt day_str with + | Some day when 1 <= day && day <= 25 -> day + | _ -> failwith "Day must be between 1 - 25") ;; -let part = - let doc = "Part to run (1 or 2)" in - let parts = [ "1", 1; "2", 2 ] in - Arg.required - & Arg.opt (Arg.some & Arg.enum parts) None - & Arg.info [ "p"; "part" ] ~docv:"PART" ~doc +let part_arg = + Command.Arg_type.create (fun part_str -> + match Int.of_string_opt part_str with + | Some part when part = 1 || part = 2 -> part + | _ -> failwith "Part must be 1 or 2") ;; -let aoc_t = Term.const Runner.run $ year $ day $ part - -let cmd = - let doc = "Run aoc solution" in - let info = Cmd.info "aoc" ~version:"1.0.0" ~doc in - Cmd.v info aoc_t +let command_param = + let open Command.Let_syntax in + let module P = Command.Param in + let%map year = + P.flag + "year" + (P.optional_with_default 2022 year_arg) + ~doc:"int year to run (default: 2022)" + ~aliases:[ "y" ] + and day = + P.flag "day" (P.required day_arg) ~aliases:[ "d" ] ~doc:"int day to run (1 - 25)" + and part = + P.flag "part" (P.required part_arg) ~aliases:[ "p" ] ~doc:"int part to run (1 or 2)" + in + fun _ -> Runner.run year day part ;; -let () = Stdlib.exit (Cmd.eval cmd) +let () = Command_unix.run (Command.basic command_param ~summary:"Run aoc solution") diff --git a/dune-project b/dune-project index ad036fb..72d2bfd 100644 --- a/dune-project +++ b/dune-project @@ -22,6 +22,6 @@ angstrom core - cmdliner + core_unix ppx_jane re)) diff --git a/lib/runner/dune b/lib/runner/dune index 8444f19..183dad5 100644 --- a/lib/runner/dune +++ b/lib/runner/dune @@ -4,5 +4,8 @@ core year2022 year2023) + (preprocess + (pps + ppx_jane)) (flags -open Core)) diff --git a/lib/util/list.mli b/lib/util/list.mli deleted file mode 100644 index 0c40c99..0000000 --- a/lib/util/list.mli +++ /dev/null @@ -1,3 +0,0 @@ -val max_int : int list -> int -val sum_int : int list -> int -val take : int -> 'a list -> 'a list diff --git a/lib/util/parser.ml b/lib/util/parser.ml index a6fc15b..59c7b57 100644 --- a/lib/util/parser.ml +++ b/lib/util/parser.ml @@ -1,27 +1,34 @@ -module A = Angstrom +include Angstrom module Syntax = struct - module Let_syntax = A.Let_syntax.Let_syntax + module Let_syntax = Angstrom.Let_syntax.Let_syntax - let ( let+ ) = A.( let+ ) - let ( and+ ) = A.( and+ ) - let ( let* ) = A.( let* ) + let ( let+ ) = ( let+ ) + let ( and+ ) = ( and+ ) + let ( let* ) = ( let* ) let ( >>| ), ( *> ), ( <* ), ( <|> ), ( <$> ) = - A.(( >>| ), ( *> ), ( <* ), ( <|> ), ( <$> )) + ( >>| ), ( *> ), ( <* ), ( <|> ), ( <$> ) ;; let ( $> ) p a = p >>| const a let ( <$ ) a p = p >>| const a end -open Syntax - -let integerP = +let integer = + let open Syntax in let%map tokens = - A.take_while1 (function + take_while1 (function | '0' .. '9' -> true | _ -> false) in Int.of_string tokens ;; + +let parse_exn parser input = + input |> parse_string ~consume:Prefix parser |> Result.ok_or_failwith +;; + +let parse_all_exn parser input = + input |> parse_string ~consume:All parser |> Result.ok_or_failwith +;; diff --git a/lib/util/parser.mli b/lib/util/parser.mli deleted file mode 100644 index 103d45a..0000000 --- a/lib/util/parser.mli +++ /dev/null @@ -1,20 +0,0 @@ -module A = Angstrom - -module Syntax : sig - module Let_syntax : sig - include module type of A.Let_syntax.Let_syntax - end - - val ( let+ ) : 'a A.t -> ('a -> 'b) -> 'b A.t - val ( and+ ) : 'a A.t -> 'b A.t -> ('a * 'b) A.t - val ( let* ) : 'a A.t -> ('a -> 'b A.t) -> 'b A.t - val ( *> ) : 'a A.t -> 'b A.t -> 'b A.t - val ( <* ) : 'a A.t -> 'b A.t -> 'a A.t - val ( >>| ) : 'a A.t -> ('a -> 'b) -> 'b A.t - val ( <$> ) : ('a -> 'b) -> 'a A.t -> 'b A.t - val ( <|> ) : 'a A.t -> 'a A.t -> 'a A.t - val ( $> ) : 'a A.t -> 'b -> 'b A.t - val ( <$ ) : 'b -> 'a A.t -> 'b A.t -end - -val integerP : int A.t diff --git a/lib/year2022/day04.ml b/lib/year2022/day04.ml index f071248..c7deaed 100644 --- a/lib/year2022/day04.ml +++ b/lib/year2022/day04.ml @@ -1,5 +1,4 @@ module P = Util.Parser -module A = Angstrom open P.Syntax type range = @@ -7,19 +6,19 @@ type range = ; stop : int } -let rangeP = - let%map start = P.integerP <* A.char '-' - and stop = P.integerP in +let range_p = + let%map start = P.integer <* P.char '-' + and stop = P.integer in { start; stop } ;; -let rangePairP = - let%map a = rangeP <* A.char ',' - and b = rangeP in +let range_pair_p = + let%map a = range_p <* P.char ',' + and b = range_p in a, b ;; -let rangePairsP = A.sep_by (A.char '\n') rangePairP +let range_pairs_p = P.sep_by (P.char '\n') range_pair_p let is_subset a b = (a.start <= b.start && a.stop >= b.stop) || (b.start <= a.start && b.stop >= a.stop) @@ -31,16 +30,14 @@ let is_overlapping a b = let part1 input = input - |> A.parse_string ~consume:Prefix rangePairsP - |> Result.ok_or_failwith + |> P.parse_exn range_pairs_p |> List.filter ~f:(fun (a, b) -> is_subset a b) |> List.length ;; let part2 input = input - |> A.parse_string ~consume:Prefix rangePairsP - |> Result.ok_or_failwith + |> P.parse_exn range_pairs_p |> List.filter ~f:(fun (a, b) -> is_overlapping a b) |> List.length ;; diff --git a/lib/year2022/day05.ml b/lib/year2022/day05.ml index e6a0938..343f510 100644 --- a/lib/year2022/day05.ml +++ b/lib/year2022/day05.ml @@ -1,5 +1,4 @@ module P = Util.Parser -module A = Angstrom module IntMap = Map.Make (Int) open P.Syntax @@ -16,21 +15,21 @@ type instructions = ; crates : crate list IntMap.t } -let dropLineP = A.skip_while (fun c -> not (Char.equal c '\n')) <* A.end_of_line -let noCrateP = None <$ A.string " " -let yesCrateP = Option.some <$> A.char '[' *> A.any_char <* A.char ']' -let crateP = noCrateP <|> yesCrateP -let crateLineP = A.sep_by1 (A.char ' ') crateP -let crateLinesP = A.sep_by1 A.end_of_line crateLineP <* A.end_of_line +let drop_line_p = P.skip_while (fun c -> not (Char.equal c '\n')) <* P.end_of_line +let no_crate_p = None <$ P.string " " +let yes_crate_p = Option.some <$> P.char '[' *> P.any_char <* P.char ']' +let crate_p = no_crate_p <|> yes_crate_p +let crate_line_p = P.sep_by1 (P.char ' ') crate_p +let crate_lines_p = P.sep_by1 P.end_of_line crate_line_p <* P.end_of_line -let moveP = - let%map n = A.string "move " *> P.integerP - and from = A.string " from " *> P.integerP - and to' = A.string " to " *> P.integerP in +let move_p = + let%map n = P.string "move " *> P.integer + and from = P.string " from " *> P.integer + and to' = P.string " to " *> P.integer in { n; from; to' } ;; -let movesP = A.sep_by1 A.end_of_line moveP +let moves_p = P.sep_by1 P.end_of_line move_p let pad_crates crates = let max = crates |> List.map ~f:List.length |> Util.List.max_int in @@ -48,10 +47,10 @@ let to_stacks crates = |> IntMap.of_alist_exn ;; -let instructionsP = - let%map crateLines = crateLinesP <* dropLineP <* dropLineP - and moves = movesP in - let crates = to_stacks crateLines in +let instructions_p = + let%map crate_lines = crate_lines_p <* drop_line_p <* drop_line_p + and moves = moves_p in + let crates = to_stacks crate_lines in { moves; crates } ;; @@ -63,9 +62,7 @@ let move_crate f crates { n; from; to' } = ;; let find_crates f input = - let { moves; crates } = - input |> A.parse_string ~consume:Prefix instructionsP |> Result.ok_or_failwith - in + let { moves; crates } = P.parse_exn instructions_p input in moves |> List.fold ~init:crates ~f:(move_crate f) |> Map.data diff --git a/lib/year2022/day07.ml b/lib/year2022/day07.ml index e7c0044..f263b2c 100644 --- a/lib/year2022/day07.ml +++ b/lib/year2022/day07.ml @@ -1,5 +1,4 @@ module P = Util.Parser -module A = Angstrom open P.Syntax type cmd = @@ -12,32 +11,32 @@ type fs = | File of string * int | Dir of string * fs list -let nameP = - A.take_while (function +let name_p = + P.take_while (function | 'a' .. 'z' | '.' | '/' -> true | _ -> false) ;; -let cdP = - let%map dir = A.string "$ cd " *> nameP in +let cd_p = + let%map dir = P.string "$ cd " *> name_p in Cd dir ;; -let lsP = Ls <$ A.string "$ ls" +let ls_p = Ls <$ P.string "$ ls" -let dirItemP = - let%map dir = A.string "dir " *> nameP in +let dir_item_p = + let%map dir = P.string "dir " *> name_p in DirItem dir ;; -let fileItemP = - let%map size = P.integerP <* A.char ' ' - and file = nameP in +let file_item_p = + let%map size = P.integer <* P.char ' ' + and file = name_p in FileItem (size, file) ;; -let cmdP = A.choice [ cdP; lsP; dirItemP; fileItemP ] -let cmdsP = A.sep_by1 A.end_of_line cmdP +let cmd_p = P.choice [ cd_p; ls_p; dir_item_p; file_item_p ] +let cmds_p = P.sep_by1 P.end_of_line cmd_p let build_fs cmds = let rec build_nodes nodes = function @@ -71,8 +70,7 @@ let sizes fs = let part1 input = input - |> A.parse_string ~consume:Prefix cmdsP - |> Result.ok_or_failwith + |> P.parse_exn cmds_p |> build_fs |> sizes |> List.filter ~f:(fun x -> x <= 100000) @@ -80,13 +78,7 @@ let part1 input = ;; let part2 input = - let ns = - input - |> A.parse_string ~consume:Prefix cmdsP - |> Result.ok_or_failwith - |> build_fs - |> sizes - in + let ns = input |> P.parse_exn cmds_p |> build_fs |> sizes in let goal = 30000000 - (70000000 - List.hd_exn ns) in ns |> List.tl_exn |> List.sort ~compare |> List.find_exn ~f:(fun n -> n >= goal) ;;