packages feed

language-ats-1.7.6.2: test/data/wc2.out

staload "SATS/wc.sats"
staload "SATS/memchr.sats"
staload "SATS/bytecount.sats"
staload "prelude/SATS/pointer.sats"
staload UN = "prelude/SATS/unsafe.sats"

#include "DATS/bytecount.dats"
#include "DATS/memchr.dats"

#define BUFSZ 32768

%{
size_t sub_ptr1_ptr1_size(atstype_ptr p1, atstype_ptr p2) {
  return ((char *)p1 - (char *)p2);
}
%}

extern
fn sub_ptr1_ptr1_size { l0, l1 : addr | l0 >= l1 }( p1 : ptr(l0)
                                                  , p2 : ptr(l1)
                                                  ) :<> size_t(l0-l1) =
  "ext#"

extern
castfn bounded(size_t) : [n:nat] size_t(n)

fn bptr_succ {l:addr}(p : ptr(l)) :<> ptr(l+1) =
  $UN.cast(ptr1_succ<byte>(p))

fn freadc
{ l : addr | l != null }{ sz : nat | sz >= 1 }(pf : !bytes_v(l, sz)
                                              | inp : !FILEptr1, bufsize : size_t(sz), p : ptr(l)) : size_t =
  let
    extern
    castfn as_fileref(x : !FILEptr1) :<> FILEref
    
    var n = $extfcall( size_t
                     , "fread"
                     , p
                     , sizeof<byte>
                     , bufsize
                     , as_fileref(inp)
                     )
  in
    n
  end

fn freadc_
{ l : addr | l != null }{ sz : nat | sz >= 1 }{ n : nat | n <= sz }( pf : !bytes_v(l, sz)
                                                                   | inp : !FILEptr1
                                                                   , bufsize : size_t(sz)
                                                                   , p : ptr(l)
                                                                   ) : size_t(n) =
  $UN.cast(freadc(pf | inp, bufsize, p))

implement free_st (st) =
  case+ st of
    | ~in_string () => ()
    | ~in_block_comment () => ()
    | ~line_comment() => ()
    | ~post_asterisk_in_block_comment() => ()
    | ~post_backslash_in_string() => ()
    | ~post_slash() => ()
    | ~regular() => ()
    | ~post_newline_whitespace() => ()
    | ~post_block_comment() => ()
    | ~post_tick() => ()

implement empty_file =
  @{ lines = 0, blanks = 0, comments = 0, doc_comments = 0 } : file

implement add_file (f0, f1) =
  @{ lines = f0.lines + f1.lines
   , blanks = f0.blanks + f1.blanks
   , comments = f0.comments + f1.comments
   , doc_comments = f0.doc_comments + f1.doc_comments
   } : file

fn byteview_read_as_char
{l0:addr}{m:nat}{ l1 : addr | l1 <= l0+m }(pf : !bytes_v(l0, m)
| p : ptr(l1)) : char =
  $UN.ptr0_get<char>(p)

fn count_for_loop
{ l : addr | l != null }{m:nat}{ n : nat | n <= m }(pf : !bytes_v(l, m)
                                                   | p : ptr(l), parse_st : &parse_state >> _, bufsz : size_t(n)) :
  file =
  let
    fn advance_char( c : char
                   , st : &parse_state >> _
                   , file_st : &file >> _
                   ) : void =
      case+ st of
        | regular() => 
          begin
            case+ c of
              | '\n' => ( free(st)
                        ; file_st.lines := file_st.lines + 1
                        ; st := post_newline_whitespace
                        )
              | '\'' => (free(st) ; st := post_tick)
              | '"' => (free(st) ; st := in_string)
              | '/' => (free(st) ; st := post_slash)
              | _ => ()
          end
        | in_string() => 
          begin
            case+ c of
              | '\n' => file_st.lines := file_st.lines + 1
              | '\\' => (free(st) ; st := post_backslash_in_string)
              | '"' => (free(st) ; st := regular)
              | _ => ()
          end
        | post_asterisk_in_block_comment() => 
          begin
            case+ c of
              | '/' => (free(st) ; st := post_block_comment)
              | '\n' => ( free(st)
                        ; file_st.comments := file_st.comments + 1
                        ; st := in_block_comment
                        )
              | '*' => ()
              | _ => (free(st) ; st := in_block_comment)
          end
        | in_block_comment() => 
          begin
            case+ c of
              | '*' => (free(st) ; st := post_asterisk_in_block_comment)
              | '\n' => file_st.comments := file_st.comments + 1
              | _ => ()
          end
        | line_comment() => 
          begin
            case+ c of
              | '\n' => ( free(st)
                        ; file_st.comments := file_st.comments + 1
                        ; st := post_newline_whitespace
                        )
              | _ => ()
          end
        | post_backslash_in_string() => 
          begin
            case+ c of
              | '\n' => ( free(st)
                        ; file_st.lines := file_st.lines + 1
                        ; st := in_string
                        )
              | _ => ()
          end
        | ~post_slash() => 
          begin
            case+ c of
              | '/' => st := line_comment
              | '\n' => ( file_st.lines := file_st.lines + 1
                        ; st := post_newline_whitespace
                        )
              | '*' => st := in_block_comment
              | _ => st := regular
          end
        | post_newline_whitespace() => 
          begin
            case+ c of
              | '\n' => (file_st.blanks := file_st.blanks + 1)
              | '\t' => ()
              | ' ' => ()
              | '/' => (free(st) ; st := post_slash)
              | '\'' => (free(st) ; st := post_tick)
              | '"' => (free(st) ; st := in_string)
              | _ => (free(st) ; st := regular)
          end
        | post_block_comment() => 
          begin
            // TODO: block comments at the end of a line
            case+ c of
              | '\n' => ( free(st)
                        ; file_st.comments := file_st.comments + 1
                        ; st := post_newline_whitespace
                        )
              | '/' => (free(st) ; st := post_slash)
              | '"' => (free(st) ; st := in_string)
              | _ => ()
          end
        | ~post_tick() => st := regular
    
    var res: file = empty_file
    var i: size_t
    val () = for* { i : nat | i <= m } .<i>. (i : size_t(i)) =>
        (i := bufsz ; i != 0 ; i := i - 1)
        (let
          var current_char = byteview_read_as_char(pf | add_ptr_bsz(p, i))
        in
          advance_char(current_char, parse_st, res)
        end)
    var current_char = byteview_read_as_char(pf | p)
    val () = advance_char(current_char, parse_st, res)
  in
    res
  end

fn count_lines_for_loop
{ l : addr | l != null }{m:nat}{ n : nat | n <= m }(pf : !bytes_v(l, m)
                                                   | ptr : ptr(l), bufsz : size_t(n)) : int =
  let
    var res: int = 0
    var i: size_t
    val () = for* { i : nat | i <= n } .<n-i>. (i : size_t(i)) =>
        (i := i2sz(0) ; i < bufsz ; i := i + 1)
        (let
          var current_char = byteview_read_as_char(pf | add_ptr_bsz(ptr, i))
        in
          case+ current_char of
            | '\n' => res := res + 1
            | _ => ()
        end)
  in
    res
  end

fn count_file_for_loop(inp : !FILEptr1) : int =
  let
    val (pfat, pfgc | p) = malloc_gc(g1i2u(BUFSZ))
    prval () = pfat := b0ytes2bytes_v(pfat)
    
    fun loop { l : addr | l != null }(pf : !bytes_v(l, BUFSZ)
                                     | inp : !FILEptr1, p : ptr(l)) : int =
      let
        var file_bytes = freadc(pf | inp, i2sz(BUFSZ), p)
        
        extern
        praxi lt_bufsz {m:nat} (size_t(m)) : [m <= BUFSZ] void
      in
        if file_bytes = 0 then
          0
        else
          let
            var fb_prf = bounded(file_bytes)
            prval () = lt_bufsz(fb_prf)
            var acc = count_lines_for_loop(pf | p, fb_prf)
          in
            acc + loop(pf | inp, p)
          end
      end
    
    var ret = loop(pfat | inp, p)
    val () = mfree_gc(pfat, pfgc | p)
  in
    ret
  end

fn count_file(inp : !FILEptr1) : file =
  let
    val (pfat, pfgc | p) = malloc_gc(g1i2u(BUFSZ))
    prval () = pfat := b0ytes2bytes_v(pfat)
    var init_st: parse_state = post_newline_whitespace
    
    fun loop { l : addr | l != null }(pf : !bytes_v(l, BUFSZ)
                                     | inp : !FILEptr1, st : &parse_state >> _, p : ptr(l)) : file =
      let
        var file_bytes = freadc(pf | inp, i2sz(BUFSZ), p)
        
        extern
        praxi lt_bufsz {m:nat} (size_t(m)) : [m <= BUFSZ] void
      in
        if file_bytes = 0 then
          empty_file
        else
          let
            var fb_prf = bounded(file_bytes)
            prval () = lt_bufsz(fb_prf)
            var acc = count_for_loop(pf | p, st, fb_prf)
          in
            acc + loop(pf | inp, st, p)
          end
      end
    
    var ret = loop(pfat | inp, init_st, p)
    val () = free(init_st)
    val () = mfree_gc(pfat, pfgc | p)
  in
    ret
  end