packages feed

language-ats-0.2.0.0: test/data/cli.out

#include "share/atspre_staload.hats"

staload "libats/ML/SATS/string.sats"
staload "libats/ML/DATS/string.dats"

datavtype flag_result =
  | present of bool
  | one of Strptr1
  | many of List_vt(Strptr1)

vtypedef flag = @{ short = charNZ
                 , long = Strptr1
                 , metavar = Option_vt(Strptr1)
                 , help = Strptr1
                 , return = flag_result
                 }
vtypedef parser(a: vt@ype+) =
  @{ modify = Strptr1 -<lincloptr1> (a, Strptr1) }

fn run_parser {a:vt@ype+}(p : parser(a), s : Strptr1) : a =
  let
    val (x, _) = p.modify(s)
  in
    x
  end

// Type for the parsed command-line arguments.
vtypedef command_line = @{ version = bool
                         , help = bool
                         , no_table = bool
                         , no_parallel = bool
                         , excludes = [m:nat] list(string, m)
                         , includes = [m:nat] list(string, m)
                         }

fun version() : void =
  println!("polygot version 0.3.31\nCopyright (c) 2018 Vanessa McHale")

fun help() : void =
  print("polyglot - Count lines of code quickly.
\33[36mUSAGE:\33[0m poly [DIRECTORY] ... [OPTION] ...
\33[36mFLAGS:\33[0m
    -V, --version            show version information
    -h, --help               display this help and exit
    -e, --exclude            exclude a directory
    -p, --no-parallel        do not execute in parallel
    -t, --no-table           display results in alternate format
                                                                                                                                                                  
    When no directory is provided poly will execute in the
    current directory.
                                                                                                                                                                  
    Bug reports and updates: github.com/vmchale/polyglot\n")

fun is_flag(s : string) : bool =
  string_is_prefix("-", s)

fn bad_exclude(s : string) : void =
  ( println!("\33[31mError:\33[0m flag " + s + " must be followed by an argument")
  ; exit(0)
  ; ()
  )

fun process_short { s : int | s > 0 }( s : string(s)
                                     , acc : command_line
                                     ) : command_line =
  let
    var str = string_make_substring(s, i2sz(0), i2sz(1))
    var acc_r = ref<command_line>(acc)
    val () = case+ str of
      | "h" => acc_r->help := true
      | "p" => acc_r->no_parallel := true
      | "t" => acc_r->no_table := true
      | "e" => bad_exclude(s)
      | "V" => acc_r->version := true
      | "-" => ()
      | _ => ( println!("\33[31mError:\33[0m flag '" + s + "' not recognized")
             ; exit(0)
             ; ()
             )
    
    fn witness(s : string) : [ n : nat | n > 0 ] string(n) =
      $UN.cast(s)
    
    val inter = !acc_r
  in
    if length(s) > 1 then
      process_short(witness(string_make_substring( s
                                                 , i2sz(1)
                                                 , length(s)
                                                 )), inter)
    else
      inter
  end

fun process(s : string, acc : command_line, is_first : bool) :
  command_line =
  let
    var acc_r = ref<command_line>(acc)
    val () = if is_flag(s) then
      case+ s of
        | "--help" => acc_r->help := true
        | "-h" => acc_r->help := true
        | "--no-table" => if not(acc.no_table) then
          acc_r->no_table := true
        else
          ( println!("\33[31mError:\33[0m flag " + s + " cannot appear twice")
          ; exit(0)
          ; ()
          )
        | "-t" => if not(acc.no_table) then
          acc_r->no_table := true
        else
          ( println!("\33[31mError:\33[0m flag " + s + " cannot appear twice")
          ; exit(0)
          ; ()
          )
        | "--no-parallel" => acc_r->no_parallel := true
        | "-p" => acc_r->no_parallel := true
        | "--version" => acc_r->version := true
        | "-V" => acc_r->version := true
        | "-e" => bad_exclude(s)
        | "--exclude" => bad_exclude(s)
        | _ => ( println!("\33[31mError:\33[0m flag '" + s + "' not recognized")
               ; exit(0)
               ; ()
               )
    else
      if not(is_first) then
        acc_r->includes := list_cons(s, acc.includes)
      else
        ()
  in
    !acc_r
  end

fun process_excludes(s : string, acc : command_line) : command_line =
  let
    var acc_r = ref<command_line>(acc)
    val () = if is_flag(s) then
      ( println!("Error: flag " + s + " found where a directory name was expected")
      ; exit(0)
      ; ()
      )
    else
      acc_r->excludes := list_cons(s, acc.excludes)
  in
    !acc_r
  end

fnx get_cli { n : int | n >= 1 }{ m : nat | m < n } .<n-m>.
( argc : int(n)
, argv : !argv(n)
, current : int(m)
, prev_is_exclude : bool
, acc : command_line
) : command_line =
  let
    var arg = argv[current]
  in
    if current < argc - 1 then
      if arg != "--exclude" && arg != "-e" then
        let
          val c = get_cli(argc, argv, current + 1, false, acc)
        in
          if prev_is_exclude && current != 0 then
            process_excludes(arg, c)
          else
            if current != 0 then
              process(arg, c, current = 0)
            else
              c
        end
      else
        let
          val c = get_cli(argc, argv, current + 1, true, acc)
        in
          c
        end
    else
      if prev_is_exclude then
        process_excludes(arg, acc)
      else
        process(arg, acc, current = 0)
  end