#line 407 "/home/ubuntu/felix/src/packages/flx_doc.fdoc"
  open Regdef;
  
  // command translation
  regdef ident_r = perl("[A-Za-z_][A-Za-z_0-9]*");
  regdef fkey_r = ident_r "." ident_r;
  regdef cmd_name_r = perl("[A-Za-z_][A-Za-z_0-9]*| *");
  regdef spc_r = " " *;
  regdef any_r = perl(".*");
  regdef cmd_r = "@" group(cmd_name_r) spc_r group(any_r);
  regdef tangler_r = "@tangler" spc_r group(fkey_r) spc_r  "=" spc_r group(any_r);
  regdef url_r = group(any_r) '<a href="' group(any_r) '">' group(any_r) "</a>" group(any_r);
  
  // top level class
  regdef class_r = ("open" spc_r)? ("class"|"module") spc_r group(ident_r) any_r;
  
  // nested in class, exactltly 2 spaces in
  regdef def_r ="ctor"|"fun"|"proc"|"gen"|"type"|"union"|"struct"|"cstruct"|"const"|"header"|"typedef";
  regdef adj_r = "virtual" | "inline";
  regdef fun_r = "  " (adj_r spc_r)? group(def_r) spc_r group(ident_r) any_r;
  
  var cmd_R = RE2 (render cmd_r);
  var tangler_R = RE2 (render tangler_r);
  var url_R = RE2 (render url_r);
  var fun_R = RE2 (render fun_r);
  var class_R = RE2 (render class_r);
  
  typedef markup_t = (`Txt | `At | `Code | `Slosh | `Math | `MathSlosh);
  fun code_fixer (a:string): string =
  {
    var out = "";
    var mode = (#`Txt) :>> markup_t;
    for ch in a do
      match mode with
      | `Txt =>
        if ch == char "@" do
          mode = (#`At) :>> markup_t;
        elif ch == char "\\" do
          mode = (#`Slosh) :>> markup_t;
        else
          out += ch;
        done
  
      | `Slosh =>
        if ch == char "(" do
          mode = (#`Math) :>> markup_t;
          out += ":math:`";
        else
          out += "\\" + ch;
          mode = (#`Txt) :>> markup_t;
        done
  
      | `Math =>
        if ch == char "\\" do
          mode = (#`MathSlosh) :>> markup_t;
        else
          out+= ch;
        done
  
      | `MathSlosh =>
         if ch == ")" do
           out+="` ";
           mode = (#`Txt) :>> markup_t;
         else
           out+="\\" + ch;
           mode = (#`Math) :>> markup_t;
         done
  
      | `At =>
        if ch == char "{" do
          out += " :code:`";
          mode = (#`Code) :>> markup_t;
        else
         out += "@"+ch;
        done
  
      | `Code =>
        if ch == char "}" do
          out += "`";
          mode = (#`Txt) :>> markup_t;
        else
          out += ch;
        done
      endmatch;
    done
    return out;
  }
  
  
  fun url_fixer (a:string) =>
    match Match (url_R, a) with
    | None => a
    | Some grp => grp.1 + "`" + grp.3 + " <" + grp.2 + ">`_" + grp.4
  ;
  
  fun code_markup(a:string) => code_fixer (url_fixer a);
  
  fun lexer_from_filename (var s:string) : string =
  {
    s = strip s;
    var lexer =
      match s.Filename::get_extension with
      | (".cpp" | ".cxx" | ".hpp")  =>  "cpp"
      | (".flx" | ".fdoc" | ".fsyn")  =>  "felix"
      | (".fpc") => "fpc"
      | (".c" | ".h") => "c"
      | (".py") => "python"
      | _ => "text"
      endmatch
    ;
    return lexer;
  }
  
  
  typedef mode_t = (`Doc | `Code | `Tangler);
  
  fun process_file (f: string): string =
  {
    var tanglers = Empty[string * string];
  
    var code_buf = Empty[string];
    var prefix = "";
    var out = "";
    proc emit_code () {
      var b = unbox (rev code_buf);
      for l in b do
        var rc = Match (class_R, l);
        var rf = Match (fun_R, l);
        chainmatch rc with
        | Some grp =>
          out+= ".. index:: " + grp.1+"(class)" + "\n";
        ormatch rf with
        | Some grp =>
          out+= ".. index:: " + grp.2+"("+grp.1+")" + "\n";
        | None => ;
        endmatch;
      done
      out += prefix;
      for l in b perform out += "  " + l + "\n";
      code_buf = Empty[string];
      mode = (#`Doc) :>> mode_t;
    }
  
    proc println[T with Str[T]] (x:T) => out += x.str + "\n";
  
    var mode : mode_t = (#`Doc) :>> mode_t;
    nextline: for line in split (f, char "\n") do
      var cmd = Match (tangler_R, line);
      match cmd with
      | Some grp =>
        mode = (#`Tangler) :>> mode_t;
        tanglers = (grp.1,grp.2) ! tanglers;
        continue nextline;
  
      | None =>
        match mode with
        | `Tangler =>
          var tab = unbox (rev tanglers);
          tanglers = Empty[string * string];
          var lkey,lfile = fold_left
            (fun (lkey:int,lfile:int) (key:string,file:string) =>
               max (lkey, key.len.int), max (lfile, file.len.int)
            )
            (10,20)
            tab
          ;
          var tabline = "=" * lkey + " " + "=" * lfile;
          println$ tabline;
          println$
            ("key" + " " * lkey).[0..lkey] +
            ("file" + " " * lfile).[0..lfile]
          ;
          println$ tabline;
          for item in tab do
            var key,file = item;
            println$
              (key + " " * lkey).[0..lkey] +
              (file + " " * lfile).[0..lfile]
            ;
          done
          println$ tabline;
          mode = (#`Doc) :>> mode_t;
        | _ => ;
        endmatch;
      endmatch;
  
      cmd = Match (cmd_R, line);
      match cmd with
      | Some grp =>
        var c = grp.1;
        var a = grp.2;
        if c == "title" do
          println$ "";
          match mode with
          | `Code () => emit_code();
          | _ => ;
          endmatch;
          a = code_markup a;
          println$ "=" * a.len.int;
          println$ a;
          println$ "=" * a.len.int;
          println$ "";
  
        elif c == "h1" do
          println$ "";
          match mode with
          | `Code () => emit_code();
          | _ => ;
          endmatch;
          a = code_markup a;
          println$ a;
          println$ "=" * a.len.int;
          println$ "";
  
        elif c == "h2" do
          a = code_markup a;
          println$ "";
          match mode with
          | `Code => emit_code();
          | _ => ;
          endmatch;
          println$ a;
          println$ "-" * a.len.int;
          println$ "";
  
        elif c == "image" do
          println$ "";
          match mode with
          | `Code => emit_code();
          | _ => ;
          endmatch;
          println$ "";
          println$ ".. image:: " + a;
          println$ "";
  
  
        elif c == "tangle" do
          println$ "";
          var lexer = lexer_from_filename a;
          prefix = ".. code-block:: "+lexer + "\n\n";
          prefix += "";
          if lexer in ("c","cpp","felix","fpc") do
            prefix += "  //[" + a + "]\n";
          elif lexer == "python" do
            prefix += "  #["+a+"]\n";
          done
          mode = (#`Code) :>> mode_t;
        else
          match mode with
          | `Code => emit_code();
          | _ => ;
          endmatch;
        done
  
  
      | None =>
        match mode with
        | `Doc =>
           println$ code_markup line;
        | `Code => code_buf = line ! code_buf;
        endmatch;
      endmatch;
    done
    if not code_buf.is_empty call emit_code();
    return out;
  }
  
  
  include "std/felix/flx_cp";
  
  var dir = "src/packages";
  var regex = "(.*).fdoc";
  var target = "doc/packages/${1}.rst";
  var live = true;
  var verbose = true;
  
  gen sandr (src: string, dst:string) =
  {
    var text = load src;
    var result = process_file (text);
    result = "Package: " + src + "\n\n"+result;
    save (dst, result);
    return true;
  }
  
  var filere = Re2::RE2 regex;
  CopyFiles::processfiles sandr (dir, filere, target, live, verbose);
  System::exit(0);