commit ab009ec9af373ee6a2948147136da75f9cc9d368 Author: atagen Date: Wed Mar 18 15:40:47 2026 +1100 init diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e66774 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +/_build +/.direnv diff --git a/bin/.ocamlformat b/bin/.ocamlformat new file mode 100644 index 0000000..e69de29 diff --git a/bin/dune b/bin/dune new file mode 100644 index 0000000..369fe51 --- /dev/null +++ b/bin/dune @@ -0,0 +1,4 @@ +(executable + (public_name inshellah-parser) + (name main) + (libraries inshellah_parser)) diff --git a/bin/main.ml b/bin/main.ml new file mode 100644 index 0000000..81c3007 --- /dev/null +++ b/bin/main.ml @@ -0,0 +1,171 @@ +open Inshellah_parser.Parser + +let () = + let _cp = + {| + -A, --show-all equivalent to -vET + -b, --number-nonblank number nonempty output lines, overrides -n + -e equivalent to -vE + -E, --show-ends display $ or ^M$ at end of each line + -n, --number number all output lines + -s, --squeeze-blank suppress repeated empty output lines + -t equivalent to -vT + -T, --show-tabs display TAB characters as ^I + -u (ignored) + -v, --show-nonprinting use ^ and M- notation, except for LFD and TAB + --help + display this help and exit + --version + output version information and exit +|} + and ls = + {| + -a, --all + do not ignore entries starting with . + -A, --almost-all + do not list implied . and .. + --author + with -l, print the author of each file + -b, --escape + print C-style escapes for nongraphic characters + --block-size=SIZE + with -l, scale sizes by SIZE when printing them; + e.g., '--block-size=M'; see SIZE format below + -B, --ignore-backups + do not list implied entries ending with ~ + -c + with -lt: sort by, and show, ctime + (time of last change of file status information); + with -l: show ctime and sort by name; + otherwise: sort by ctime, newest first + -C + list entries by columns + --color[=WHEN] + color the output WHEN; more info below + -d, --directory + list directories themselves, not their contents + -D, --dired + generate output designed for Emacs' dired mode + -f + same as -a -U + -F, --classify[=WHEN] + append indicator (one of */=>@|) to entries WHEN + --file-type + like -F, except do not append '*' + --format=WORD + across,horizontal (-x), commas (-m), long (-l), + single-column (-1), verbose (-l), vertical (-C) + --full-time + like -l --time-style=full-iso + -g + like -l, but do not list owner + --group-directories-first + group directories before files + -G, --no-group + in a long listing, don't print group names + -h, --human-readable + with -l and -s, print sizes like 1K 234M 2G etc. + --si + likewise, but use powers of 1000 not 1024 + -H, --dereference-command-line + follow symbolic links listed on the command line + --dereference-command-line-symlink-to-dir + follow each command line symbolic link that points to a directory + --hide=PATTERN + do not list implied entries matching shell PATTERN + (overridden by -a or -A) + --hyperlink[=WHEN] + hyperlink file names WHEN + --indicator-style=WORD + append indicator with style WORD to entry names: + none (default), slash (-p), file-type (--file-type), classify (-F) + -i, --inode + print the index number of each file + -I, --ignore=PATTERN + do not list implied entries matching shell PATTERN + -k, --kibibytes + default to 1024-byte blocks for file system usage; + used only with -s and per directory totals + -l + use a long listing format + -L, --dereference + when showing file information for a symbolic link, + show information for the file the link references + rather than for the link itself + -m + fill width with a comma separated list of entries + -n, --numeric-uid-gid + like -l, but list numeric user and group IDs + -N, --literal + print entry names without quoting + -o + like -l, but do not list group information + -p, --indicator-style=slash + append / indicator to directories + -q, --hide-control-chars + print ? instead of nongraphic characters + --show-control-chars + show nongraphic characters as-is; + the default, unless program is 'ls' and output is a terminal + -Q, --quote-name + enclose entry names in double quotes + --quoting-style=WORD + use quoting style WORD for entry names: + literal, locale, shell, shell-always, + shell-escape, shell-escape-always, c, escape + (overrides QUOTING_STYLE environment variable) + -r, --reverse + reverse order while sorting + -R, --recursive + list subdirectories recursively + -s, --size + print the allocated size of each file, in blocks + -S + sort by file size, largest first + --sort=WORD + change default 'name' sort to WORD: + none (-U), size (-S), time (-t), + version (-v), extension (-X), name, width + --time=WORD + select which timestamp used to display or sort; + access time (-u): atime, access, use; + metadata change time (-c): ctime, status; + modified time (default): mtime, modification; + birth time: birth, creation; + with -l, WORD determines which time to show; + with --sort=time, sort by WORD (newest first) + --time-style=TIME_STYLE + time/date format with -l; see TIME_STYLE below + -t + sort by time, newest first; see --time + -T, --tabsize=COLS + assume tab stops at each COLS instead of 8 + -u + with -lt: sort by, and show, access time; + with -l: show access time and sort by name; + otherwise: sort by access time, newest first + -U + do not sort directory entries + -v + natural sort of (version) numbers within text + -w, --width=COLS + set output width to COLS. 0 means no limit + -x + list entries by lines instead of by columns + -X + sort alphabetically by entry extension + -Z, --context + print any security context of each file + --zero + end each output line with NUL, not newline + -1 + list one file per line + --help + display this help and exit + --version + output version information and exit + |} + in + match parse_help ls with + | Ok entries -> List.iter (fun e -> print_entry e) entries + | Error msg -> Printf.eprintf "parse error: %s\n" msg diff --git a/dune-project b/dune-project new file mode 100644 index 0000000..b904d6a --- /dev/null +++ b/dune-project @@ -0,0 +1,31 @@ +(lang dune 3.20) + +(name inshellah-parser) + +(generate_opam_files true) + +(source + (github username/reponame)) + +(authors "Author Name ") + +(maintainers "Maintainer Name ") + +(license LICENSE) + +(documentation https://url/to/documentation) + +(package + (name inshellah-parser) + (synopsis "A short synopsis") + (description "A longer description") + (depends + ocaml + dune + angstrom + angstrom-unix + (ppx_inline_test :with-test)) + (tags + ("add topics" "to describe" your project))) + +; See the complete stanza docs at https://dune.readthedocs.io/en/stable/reference/dune-project/index.html diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..3adb309 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1773385838, + "narHash": "sha256-ylF2AGl08seexxlLvMqj3jd+yZq56W9zicwe51mp0Pw=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "fef542e7a88eec2b698389e6279464fd479926b6", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..e475c84 --- /dev/null +++ b/flake.nix @@ -0,0 +1,32 @@ +{ + inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + + outputs = + { self, nixpkgs }: + let + forAllSystems = + f: + nixpkgs.lib.genAttrs [ "x86_64-linux" "aarch64-linux" ] ( + system: f (import nixpkgs { inherit system; }) system + ); + in + { + devShells = forAllSystems ( + pkgs: sys: { + default = pkgs.mkShell { + packages = with pkgs.ocamlPackages; [ + dune_3 + ocaml + angstrom + angstrom-unix + ppx_inline_test + ocaml-lsp + ocamlformat + ocamlformat-rpc-lib + utop + ]; + }; + } + ); + }; +} diff --git a/inshellah-parser.opam b/inshellah-parser.opam new file mode 100644 index 0000000..726c5a9 --- /dev/null +++ b/inshellah-parser.opam @@ -0,0 +1,35 @@ +# This file is generated by dune, edit dune-project instead +opam-version: "2.0" +synopsis: "A short synopsis" +description: "A longer description" +maintainer: ["Maintainer Name "] +authors: ["Author Name "] +license: "LICENSE" +tags: ["add topics" "to describe" "your" "project"] +homepage: "https://github.com/username/reponame" +doc: "https://url/to/documentation" +bug-reports: "https://github.com/username/reponame/issues" +depends: [ + "ocaml" + "dune" {>= "3.20"} + "angstrom" + "angstrom-unix" + "ppx_inline_test" {with-test} + "odoc" {with-doc} +] +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] +dev-repo: "git+https://github.com/username/reponame.git" +x-maintenance-intent: ["(latest)"] diff --git a/lib/.ocamlformat b/lib/.ocamlformat new file mode 100644 index 0000000..e69de29 diff --git a/lib/dune b/lib/dune new file mode 100644 index 0000000..6c2da6b --- /dev/null +++ b/lib/dune @@ -0,0 +1,3 @@ +(library + (name inshellah_parser) + (libraries angstrom angstrom-unix)) diff --git a/lib/parser.ml b/lib/parser.ml new file mode 100644 index 0000000..5d4478b --- /dev/null +++ b/lib/parser.ml @@ -0,0 +1,77 @@ +(* open Angstrom_unix *) +(* also look for "subcommands" for clapslop *) +(* and other common help patterns *) +open Angstrom + +let ( <| ) = ( @@ ) +let ( <&> ) p1 p2 = lift2 (fun a b -> (a, b)) p1 p2 +let is_whitespace = function ' ' | '\t' | '\n' | '\r' -> true | _ -> false + +let is_alphanumeric = function + | 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' -> true + | _ -> false + +let is_long_char = function + | 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' | '-' -> true + | _ -> false + +type switch = Short of char | Long of string | Both of char * string +type param = Mandatory of string | Optional of string +type entry = { switch : switch; param : param option; desc : string } + +let whitespace = skip_while is_whitespace +let comma = char ',' *> whitespace +let short_switch = char '-' *> satisfy is_alphanumeric +let long_switch = string "--" *> take_while1 is_long_char + +let opt_param = + print_endline "opt param is running"; + string "[=" *> take_while is_alphanumeric <* char ']' >>| fun a -> Optional a + +let man_param = + print_endline "man param is running"; + char '=' *> take_while is_alphanumeric >>| fun a -> Mandatory a + +let param_parser = + option None (choice [ opt_param; man_param ] >>| fun a -> Some a) + +let switch_parser = + choice + [ + (* -a, --all *) + ( short_switch >>= fun s -> + comma *> long_switch >>| fun l -> Both (s, l) ); + (* -a *) + (short_switch >>| fun s -> Short s); + (* --all *) + (long_switch >>| fun l -> Long l); + ] + +let description = whitespace *> take_till (fun c -> c = '\n') <* end_of_line + +let entry = + skip_while (fun c -> c <> '-') + *> lift3 (fun a b c -> (a, b, c)) switch_parser param_parser description + >>| fun (switch, param, desc) -> { switch; param; desc } + +let endline = option () (char '\n' *> return ()) +let entry_line = entry <* endline +let help_parser = many entry_line + +let parse_help txt = + Angstrom.parse_string ~consume:Consume.Prefix help_parser txt + +let print_switch = function + | Short o -> Printf.sprintf "Short: %c" o + | Long o -> Printf.sprintf "Long: %s" o + | Both (s, l) -> Printf.sprintf "Both, short: %c long: %s" s l + +let print_opt = function + | Some (Mandatory o) -> Printf.sprintf "Mandatory: %s" o + | Some (Optional o) -> Printf.sprintf "Optional: %s" o + | None -> "None" + +let print_entry e = + Printf.printf + "\n\t** ENTRY **\n\tSwitch: %s\n\tParam: %s\n\tDescription: %s\n" + (print_switch e.switch) (print_opt e.param) e.desc diff --git a/test/dune b/test/dune new file mode 100644 index 0000000..20b5aab --- /dev/null +++ b/test/dune @@ -0,0 +1,2 @@ +(test + (name test_inshellah_parser)) diff --git a/test/test_inshellah_parser.ml b/test/test_inshellah_parser.ml new file mode 100644 index 0000000..e69de29