PreviousUpNext

15.4.14  src/app/c-glue-maker/gen.pkg

# gen.pkg - Generating and pretty-printing
#           Mythryl code implementing a
#           typed interface to a C program.
#
#  (C) 2004  The Fellowship of SML/NJ
#
# author: Matthias Blume (blume@tti-c.org)

# Compiled by:
#     src/app/c-glue-maker/c-glue-maker.lib

# See ../README for an overview, and
# ../c-glue-lib/doc/* for additional info.
#
# This file is the heart of the c-glue-maker application:
# main::main (from ./main.pkg) calls our
# entrypoint gen::gen with the digested commandline
# switch info plus the list of C source files
# to be processed.
#
# fun 'gen' constitutes >90% of this file.
#
# The basic sequence of events in this file
# is pretty simple:
#
#  o We call the c-kit parser to parse the given
#    C .h header file(s).
#
#  o We call 'build' in ast-to-spec.pkg to convert
#    the C parse trees into our (simpler) 'spec'
#    working format, defined in spec.pkg
#
#  o We do various good magic to convert these
#    C declarations into abstract Mythryl equivalents.
#    This logic occupies roughly the first half
#    of this file.
#
#  o We prettyprint the abstract Mythryl declarations
#    out as actual text Mythryl source files.
#    This logic occupies roughly the last half
#    of this file.
#
#  o Finally, we spit out a .lib makefile to
#    compile the generated Mythryl sourcefiles.
#
# Calltree backbone:
#
#    gen
#        get_spec cfile
#            cfile' =   preprocess_c_sourcefile                              cfile ;
#            ast    =   parse_to_raw_syntax_tree::file_to_raw_syntax_tree'   cfile';
#            specs  =   raw_syntax_tree_to_spec::build                       ast   ;


###                  "The first condition of understanding
###                   a foreign country is to smell it."
###
###                                    -- Rudyard Kipling



stipulate
    package fil =  file__premicrothread;                                # file__premicrothread  is from   src/lib/std/src/posix/file--premicrothread.pkg

    program = "c-glue-maker";
    version = "0.9.1";
    author  = "Matthias Blume";
    email   = "blume@tti-c.org";

    package s= spec;                    # spec  is from   src/app/c-glue-maker/spec.pkg

herein

    package gen : api {
                         version:  String;

                         gen:  { cfiles:         List( String ),        # List of C .h files from the commandline.
                                 match:          String -> Bool,        # Regex from commandline -match switch -- see ./README
                                 dirname:        String,
                                 makelib_file:     String,
                                 prefix:         String,
                                 gensym_stem:    String,
                                 extra_members:  List( String ),
                                 library_handle: String,

                                 all_su:         Bool,
                                 mythryl_options: List( String ),
                                 noguid:         Bool,
                                 wid:            Int,
                                 weightreq:      Null_Or( Bool ),       #  THE TRUE -> heavy, THE FALSE -> light, NULL -> both
                                 namedargs:      Bool,
                                 collect_enums:  Bool,
                                 enumcons:       Bool,

                                 preprocess_c_sourcefile:   String -> String,

                                 target:  { name:   String,
                                            sizes:  sizes::Sizes,       # sizes is from   src/lib/c-kit/src/ast/sizes.pkg
                                            shift:  (Int, Int, Unt) -> Unt
                                          }
                               }
                               ->
                               Void;
                    }
    {
        version = version;

        package out =  plain_file_prettyprint_output_stream_avoiding_pointless_file_rewrites;   # plain_file_prettyprint_output_stream_avoiding_pointless_file_rewrites is from   src/lib/prettyprint/big/src/out/plain-file-prettyprint-output-stream-avoiding-pointless-file-rewrites.pkg
        package p   =  prettyprint;                                                             # prettyprint                                                                   is from   src/app/c-glue-maker/prettyprint.pkg
        package pp  =  plain_file_prettyprinter_avoiding_pointless_file_rewrites;               # plain_file_prettyprinter_avoiding_pointless_file_rewrites                     is from   src/lib/prettyprint/big/src/plain-file-prettyprinter-avoiding-pointless-file-rewrites.pkg
        package ss  =  string_set;                                                              # string_set                                                                    is from   src/lib/src/string-set.pkg
        package sm  =  string_map;                                                              # string_map                                                                    is from   src/lib/src/string-map.pkg
        package im  =  int_red_black_map;                                                       # int_red_black_map                                                             is from   src/lib/src/int-red-black-map.pkg

        # "lis" == "Large-Integer Set":
        #
        package lis
            =
            red_black_set_g (
                Key = large_int::Int;           # large_int             is from   src/lib/std/large-int.pkg
                compare = large_int::compare;
            );

        tuple = p::TUPLE;

        fun record [] => p::void;
            record l  => p::RECORD l;
        end;

        type_constructor =  p::TYP;
        arrow            =  p::ARROW;
        typ              =  p::typ;             # "typ" == "type constructor". Convenience fn for type constructors with no args.
        void             =  p::void;
        etuple           =  p::ETUPLE;
        eunit            =  etuple [];

        fun erecord [] =>  p::ETUPLE [];
            erecord l  =>  p::ERECORD l;
        end;

        evar    = p::EVAR;
        eapp    = p::EAPP;
        econstr = p::ECONSTR;
        eseq    = p::ESEQ;
                                                                                # unt           is from   src/lib/std/unt.pkg
                                                                                # int           is from   src/lib/std/int.pkg
                                                                                # string        is from   src/lib/std/string.pkg
                                                                                # file__premicrothread          is from   src/lib/std/src/posix/file--premicrothread.pkg
        fun eword w =   evar ("0wx" + unt::to_string w);
        fun eint  i =   evar (int::to_string i);

        fun elint   i =   evar (large_int::to_string i);
        fun estring s =   evar (cat ["\"", string::to_string s, "\""]);

        fun warn m =   fil::write (fil::stderr, "warning: " + m);
        fun err  m =   raise exception DIE (cat ("gen: " ! m));

        fun unimp     what =   raise exception DIE ("unimplemented type: " + what);
        fun unimp_arg what =   raise exception DIE ("unimplemented argument type: " + what);
        fun unimp_res what =   raise exception DIE ("unimplemented result type: " + what);

        writeto = "write'to";

        do_not_edit =  "# Generated file -- do not edit.";

        fun make_credits platform                                    #  platform == "intel32-linux" or such.  
            =
            cat ["# [by ", author, "'s ",
                    program, " (version ", version, ") for ",
                    platform, "]"];

        comments_to
            =
            cat [ "# Send comments and suggestions to ",
                   email,
                   ". Thanks!"
                 ];



        # Fns to construct "fptr_rtti_13",
        #                  "fptr_rtti_13::type",
        #              and "fptr_rtti_13::makecall":

        fun fptr_rtti_struct_id             i =  "fptr_rtti_"           + int::to_string i;
        fun fptr_rtti_struct_id_cc_type      i =   fptr_rtti_struct_id i + "::type";
        fun fptr_rtti_struct_id_cc_makecall i =   fptr_rtti_struct_id i + "::makecall";



        # Here we make various package names:
        #   "struct_type_foo" for "struct foo {... } bar;", to go in file "incomplete-structure-foo.pkg"
        #  "sstruct_type_foo" for "struct foo {... } bar;", to go in file "global-var-bar.pkg"
        #
        #    "union_type_foo"  for "union  foo {... } bar;", to go in file "incomplete-union-foo.pkg"
        #   "uunion_type_foo"  for "union  foo {... } bar;", to go in file "global-var-bar.pkg"
        #
        #     "enum_type_foo"  for "enum   foo {... } bar;", to go in file "incomplete-enum-foo.pkg"
        #    "eenum_type_foo"  for "enum   foo {... } bar;", to go in file "global-var-bar.pkg"

        fun incomplete_sue_package_name  kind  c_name                           # 'sue' == "struct, union or enum"
            =                                                                   # 'kind' in  "struct"/"union"/"enum" or "sstruct"/"uunion"/"eenum"/...
            cat [kind, "_type_", c_name];


        ststruct =   incomplete_sue_package_name  "sstruct";
        utstruct =   incomplete_sue_package_name  "uunion";

        fun sue_tag kind c_name                                                 # 'sue' == "struct, union or enum"
            =                                                                   # 'kind' in "sstruct"/"uunion"/"eenum"
            typ (incomplete_sue_package_name  kind  c_name  +  "::Tag");        # 'c_name' is from .h file, or "'" for anonymous structs etc.





        # Fns to construct "field_type_13"
        #                  "field_rtti_13":                                     # rtti == "run time type information"

        fun fieldtype_id n =   "field_type_" + n;
        fun fieldrtti_id n =   "field_rtti_" + n;



        # Construct "field_id_foo" where "foo"
        # was a field name in the .h file -- something like
        #
        #     struct mine { int foo; };
        #
        # This will be used to name the function(s) for
        # getting/setting this field's value.
        #
        fun field_id ( c_name,          # "foo"
                       optional_prime   # "'" or ""
                     )
            =
            cat ["field_id_", c_name, optional_prime];



#       fun arg_id  s =   "arg_id_"  + s;
#       fun enum_id n =   "enum_id_" + n;

        fun arg_id  s
            =
            {   result = "arg_id_"  + s;
# print ("arg_id: s='"$s$"' result='"$result$"'\n");
                result;
            };

        fun enum_id n
            =
            { result = "enum_id_" + n;
# print ("enum_id: n='"$n$"' result='"$result$"'\n");
              result;
            };

        my @? = sm::get;                                                # "sm" == "string map"
        my %? = im::get;                                                # "im" == "integer map"

#       fun thetag (t: s::Tag) t'
#           =
#           t == t';



        fun gen arg_record                                              # Our main entrypoint.
            =
            {   arg_record
                    ->
                    { cfiles,
                      match,
                      preprocess_c_sourcefile,
                      gensym_stem,                                      # Per "-gensym" commandline switch. Default "".
                      dirname,
                      makelib_file,
                      prefix,                                           # Per "-prefix" commandline switch. Default "".
                      extra_members,
                      library_handle,
                      all_su,                                           # "su" == "structs and unions".
                      mythryl_options,
                      noguid,
                      wid,
                      weightreq,
                      collect_enums,
                      enumcons,
                      namedargs => do_arg_names,

                      target => { name => platform,                     # "intel32-linux" or such. 
                                  sizes,
                                  shift
                                }
                    };



                # The next three are used to construct
                # witness types -- see witness_type_p & kith:
                st =   sue_tag "sstruct";                               # "sue" == "struct, union or enum".
                un =   sue_tag "uunion";
                fun en (c_name, anon)                                   # "en" == "enum", likely.
                    =
                    if  (collect_enums  and  anon)
                         sue_tag "eenum" "'";
                    else sue_tag "eenum" c_name;
                    fi;


                                                                        # hash  is from   src/app/c-glue-maker/hash.pkg
                hash_cft      =   hash::make_fhasher ();                # Hash C types to integers.       ("cft" == "C function type".)
                hash_lib7type =   hash::make_thasher ();                # Hash Mythryl types to integers.

                gensym_suffix                                           # Implemement the "-gensym" commandline switch -- see ./README.
                    =
                    if (gensym_stem == "")   "";
                    else                     "_" + gensym_stem;
                    fi;



                # Construct package names:
                #  "struct_foo" for a "struct foo {... };" .h-file declaration, to go in struct-foo.pkg and/or incomplete-struct-foo.pkg,
                #   "union_foo" for a "union  foo {... };" .h-file declaration, to go in  union-foo.pkg and/or  incomplete-union-foo.pkg,
                #    "enum_foo" for a "enum   foo {... };" .h-file declaration, to go in   enum-foo.pkg and/or   incomplete-enum-foo.pkg.
                #
                # We can also get called with 'kind' of "sstruct"/"uunion"/"eenum", I don't yet know when/why.
                #
                fun sue_package_name
                        kind            # One of "struct"/"union"/"enum"; else "sstruct"/"uunion"/"eenum";
                        c_name          # foo
                    =
                    cat [prefix, kind, "_", c_name];



#               sstruct =   sue_package_name "sstruct";         # Appears to be never used.
#               ustruct =   sue_package_name "uunion";          # Appears to be never used.
                estruct =   sue_package_name "eenum";           # Called only from fun estruct', I think, in turned called only from pprint_e_pkg.



                # Construct package name "ttype_foo" where "foo"
                # was the typedef'd (or such) type name in the .h file.
                #
                # "prefix", if any, is from the "-prefix" commandline
                # switch -- see ./README:
                #
                fun package_name_for_c_type  c_name
                    =
                    cat [prefix, "ttype_", c_name];



                # Construct package name "global_var_foo" where "foo"
                # was the variable name in the .h file.
                #
                # "prefix", if any, is from the "-prefix" commandline
                # switch -- see ./README:
                #
                # This package will be defined in a file "global-var-i.pkg"
                #
                fun package_name_for_c_global_var  c_name
                    =
                    cat [prefix, "global_var_", c_name];



                # Construct package name "ffunc_foo" where "foo"
                # was the function name in the .h file.
                #
                # "prefix", if any, is from the "-prefix" commandline
                # switch -- see ./README:
                #
                # This package will be defined in a file "f-foo.pkg"
                #
                fun package_name_for_c_function c_name
                    =
                    cat [prefix, "ffunc_", c_name];



                fun estruct' (n, anon)
                    =
                    estruct (  (anon and collect_enums) ??  "'"
                                                        ::   n
                            );


                # Construct "sstructttype_foo::type" from "struct foo {... } bar;", to go in file "global-var-bar.pkg" (rtti = ... )
                #
                fun styp c_name
                    =
                    ststruct c_name  +  "::type";

                # Construct "uunionttype_foo::type" from "union  foo {... } bar;", to go in file "global-var-bar.pkg" (rtti = ... )
                #
                fun utyp c_name
                    =
                    utstruct c_name  +  "::type";

                my (do_heavy, do_light)
                    =
                    case weightreq
                        NULL      => (TRUE,  TRUE);
                        THE TRUE  => (TRUE,  FALSE);
                        THE FALSE => (FALSE, TRUE);
                    esac;

                credits =   make_credits  platform;



                # Read specs from C source file 'cfile',
                # combine them with previously known 'specs',
                # and return the result:
                #
                fun get_spec (cfile, specs)
                    =
                    {   preprocessed_c_source_code_file
                            =
                            preprocess_c_sourcefile  cfile;

                        (    {   astbundle                                      # parse_to_raw_syntax_tree      is from   src/lib/c-kit/src/ast/parse-to-ast.pkg
                                     =
                                     parse_to_raw_syntax_tree::file_to_raw_syntax_tree'

                                         fil::stderr                            # file__premicrothread          is from   src/lib/std/src/posix/file--premicrothread.pkg
                                         (sizes, state::INITIAL)                # state         is from   src/lib/c-kit/src/ast/state.pkg
                                         preprocessed_c_source_code_file;

                                 new_specs                                      # raw_syntax_tree_to_spec       is from   src/app/c-glue-maker/ast-to-spec.pkg
                                     =
                                     raw_syntax_tree_to_spec::build
                                       {
                                         bundle => astbundle,
                                         sizes,
                                         collect_enums,
                                         cfiles,
                                         match,
                                         all_su,
                                         eshift => shift,
                                         gensym_suffix
                                       };

                                 s::join (new_specs, specs);
                             }
                             except                                             # winix__premicrothread         is from   src/lib/std/winix--premicrothread.pkg
                                 e =  {   winix__premicrothread::file::remove_file   preprocessed_c_source_code_file
                                          except
                                              _ = ();

                                          raise exception e;
                                      }
                        )
                        then (
                            winix__premicrothread::file::remove_file   preprocessed_c_source_code_file
                            except
                                _ = ()
                        );
                    };



                # Read and combine specs from
                # all given C source files:
                #
                (fold_forward  get_spec  s::empty  cfiles)
                    ->
                    { structs, unions, enums,  global_variables, global_functions, global_types };



                # A thunk to make directory 'dirname'
                # if we haven't already done so:
                #
                do_dir
                    =
                    do_it
                    where
                        done =   REF FALSE;

                        fun do_it ()
                            =
                            if (not *done)
                                #
                                done := TRUE;

                                if (not (winix__premicrothread::file::is_directory     dirname
                                         except
                                            _ = FALSE
                                   )   )

                                    winix__premicrothread::file::make_directory   dirname;
                                fi;
                            fi;
                    end;

                makelib_files =   REF extra_members;    # All .pkg files that should go
                                                        # into the .lib file
                exported_packages =   REF [];


                # We don't want apostrophes in file names.
                # This fn turns them into minuses: 
                #
                fun quotes_to_minuses  some_string
                    =
                    string::translate
                        (\\ '\'' =>  "-";
                            c    =>  string::from_char c;
                         end
                        )
                        some_string;


                # This unpleasantly impure function:
                #  o creates 'dirname' directory if it doesn't exist.
                #  o makes 'nqx', a quote-free version of filename 'x',
                #  o adds "<nqx>.pkg <mythryl_options>" to string list 'makelib_files' (to go in synthesized .lib file)
                #  o returns "<dirname>/<nqx>.pkg" as its result.
                #
                fun validate_pkg_filename x
                    =
                    {   nqx =  quotes_to_minuses x;     # "nqx" == "no quotes x"

                        file   =  winix__premicrothread::path::join_base_ext  { base => nqx,  ext => THE "pkg" };
                        result =  winix__premicrothread::path::make_path_from_dir_and_file  { dir => dirname, file };

                        opts   =  if noguid      "noguid" ! mythryl_options;
                                  else           mythryl_options;
                                  fi;


                        # Collapse 'opts' from a list of strings
                        # to a single string of blank-separated components.
                        #
                        opt =  string::join'   "("   " "   ")"   opts;

                        makelib_files := file + opt ! *makelib_files;
                        do_dir ();
                        result;
                    };


                # Construct and return path "<dirname>.<file>".
                # As a side effect, make sure directory 'dirname' exists.
                #
                fun descrfile file
                    =
                    {   result =   winix__premicrothread::path::make_path_from_dir_and_file { dir => dirname, file };
                        do_dir ();
                        result;
                    };



                # Build the obvious maps from struct/union/enum tags to structs/unions/enums:

                structs
                    =
                    fold_forward
                        (\\ (s, m) =  sm::set (m, s.c_name, s))
                        sm::empty
                        structs;

                unions
                    =
                    fold_forward
                        (\\ (u, m) =  sm::set (m, u.c_name, u))
                        sm::empty
                        unions;

                enums
                    =
                    fold_forward
                        (\\ (e, m) =  sm::set (m, e.c_name, e))
                        sm::empty
                        enums;



                # Here we find all structs/unions/enums
                # recursively reachable from the toplevel
                # types exported by the given C source files:
                #
                my (structs, unions, enums)
                    =
                    {   # These three track which
                        # struct/union/enum tags have
                        # already been scheduled for
                        # processing: 
                        #
                        sdone = REF ss::empty;          # "sdone" == "structs done"
                        udone = REF ss::empty;          # "udone" == "unions  done"
                        edone = REF ss::empty;          # "edone" == "enums   done"


                        # These three map
                        # struct/union/enum tags
                        # to their corresponding 
                        # struct/union/enum:
                        #
                        smap  = REF sm::empty;          # "smap"  == "structs map"
                        umap  = REF sm::empty;          # "umap"  == "unions  map"
                        emap  = REF sm::empty;          # "emap"  == "enums   map"

                        tq =  REF [];                   # "tq" == "type queue" or "tag queue", I think.
                                                        # Anyhow, holds list of work remaining to do.

                        fun ty_sched t                  # Schedule a type for processing by adding it to type queue "tq"
                            =
                            tq := t ! *tq;



                        # Schedule an ordinary field for processing.
                        # silently ignore bitfields:
                        #
                        fun fs_sched (s::OFIELD { spec => (_, t), ... } ) =>   ty_sched t;
                            fs_sched _                                    =>   ();
                        end;
                        #
                        fun f_sched { name, spec }
                            =
                            fs_sched spec;



                        # Add something to appropriate 'done' list:
                        #   'xdone'   will be one of 'sdone', 'udone', 'edone'
                        #   'xmap'    will be one of 'smap',  'umap',  'emap'
                        #   'c_name'  is the relevant struct/union/enum name
                        #   'x'       is the struct/union/enum named by 'c_name'.
                        #   'xfields' is a fn extracting from 'x' the fields
                        #             which need processing: .fields for structs
                        #                                    .all    for unions
                        #             (Enums have no fields needing processing.)  
                        #
                        fun xenter (xdone, xall, xmap, xfields) c_name
                            =
                            if (not (ss::member (*xdone, c_name)))

                                xdone :=  ss::add (*xdone, c_name);

                                case (@? (xall, c_name))

                                    THE x => {   xmap := sm::set (*xmap, c_name, x);
                                                 apply f_sched (xfields x);
                                             };
                                    NULL => ();
                                esac;
                            fi;

                        senter =   xenter (sdone, structs, smap, .fields);
                        uenter =   xenter (udone, unions,  umap, .all);
                        eenter =   xenter (edone, enums,   emap, \\ _ = []);

                        fun sinclude (s: s::Type_Struct) =   if   (not s.exclude   )   senter s.c_name;   fi;
                        fun uinclude (u: s::Type_Union)  =   if   (not u.exclude   )   uenter u.c_name;   fi;
                        fun einclude (e: s::Type_Enum)   =   if   (not e.exclude   )   eenter e.c_name;   fi;


                        # Schedule global types, variables
                        # and functions for processing.
                        #
                        # Here 'src' is a source code region like "foo.h:4596.16-23",
                        # and 'c_name' is the type/var/fun name from the .h file.
                        #
                        fun global_type     { src, c_name, spec            } =   ty_sched spec;
                        fun global_variable { src, c_name, spec => (_, t)  } =   ty_sched t;
                        fun global_function { src, c_name, spec, arg_names } =   ty_sched (s::FPTR spec);


                        # Here we appear to be essentially calling
                        # senter/uenter/eenter on every struct/union/enum
                        # recursively reachable from work list 'tq'.
                        #
                        # We copy 'tq' to 'tl' before beginning, but
                        # our senter/uenter/eenter ops may add new stuff
                        # to 'tq', so in general we wind up doing multiple
                        # 'rounds' until nothing new is found:
                        #
                        fun loop []
                                =>
                                ();

                            loop tl
                                =>
                                {   # 'type' ("analyse_type"?) does the
                                    # recursive decomposition of a type looking
                                    # for all types referenced by it.
                                    #
                                    # Ultimately, we're only interested in
                                    # in struct/union/enum types, but we may
                                    # have to look inside pointer and function
                                    # types etc to find them:
                                    #
                                    fun type (s::STRUCT t) =>  senter t;
                                        type (s::UNION  t) =>  uenter t;

                                        type (s::ENUM (t, anon))
                                             =>
                                             if  (collect_enums and anon)   eenter "'";
                                             else                           eenter  t;
                                             fi;

                                        type (s::PTR (_, s::STRUCT t)) =>   (); # Why do we ignore 't' here?
                                        type (s::PTR (_, s::UNION  t)) =>   (); # "                        "
                                        type (s::PTR (_, t          )) =>   type t;

                                        type (s::FPTR { args, result } )
                                             =>
                                             {   apply type args;
                                                 null_or::apply type result;
                                             };

                                        type (s::ARR { t, ... } ) => type t;
                                        type (s::UNIMPLEMENTED _) => ();

                                        type ( s::SCHAR     | s::UCHAR
                                               | s::SINT      | s::UINT
                                               | s::SSHORT    | s::USHORT
                                               | s::SLONG     | s::ULONG
                                               | s::SLONGLONG | s::ULONGLONG
                                               | s::FLOAT     | s::DOUBLE
                                               | s::VOIDPTR)
                                            => ();                                      # C base types require no processing.
                                    end;

                                    fun tloop []       =>  nextround ();
                                        tloop (t ! ts) =>  {   type t;
                                                               tloop ts;
                                                           };
                                    end;

                                    tq := [];

                                    tloop tl;
                                };
                        end 

                        also
                        fun nextround ()
                                =
                                loop *tq;

                        sm::apply sinclude structs;
                        sm::apply uinclude unions;
                        sm::apply einclude enums;

                        apply  global_type      global_types;
                        apply  global_variable  global_variables;
                        apply  global_function  global_functions;

                        nextround ();

                        (*smap, *umap, *emap);
                    };

                fun stem s::SCHAR     => "Schar";
                    stem s::UCHAR     => "Uchar";
                    stem s::SINT      => "Sint";
                    stem s::UINT      => "Uint";
                    stem s::SSHORT    => "Sshort";
                    stem s::USHORT    => "Ushort";
                    stem s::SLONG     => "Slong";
                    stem s::ULONG     => "Ulong";
                    stem s::SLONGLONG => "Slonglong";
                    stem s::ULONGLONG => "Ulonglong";
                    stem s::FLOAT     => "Float";
                    stem s::DOUBLE    => "Double";
                    stem s::VOIDPTR   => "Voidptr";
                    stem _            => raise exception DIE "bad stem";
                end;

                fun insert_name (c_name, string_set)
                    =
                    if (ss::member (string_set, c_name))   string_set;
                    else   ss::add (string_set, c_name);
                    fi;



                #  Search 'structs', 'unions', 'global_types',
                #  'global_variables' and 'global_functions'
                #  for incomplete and function pointer types.
                #
                # "We don't expect many different function pointer types or
                #  incomplete types in any given C interface, so using linear
                #  lists here is probably ok." -- Matthias
                #
                my  (  fptr_types,
                       incomplete_structs,
                       incomplete_unions,
                       incomplete_enums
                    )
                    =
                    {   # "type" == "analyse_type"?  "add_type"?

                        fun type ( ( s::SCHAR     | s::UCHAR
                                   | s::SINT      | s::UINT
                                   | s::SSHORT    | s::USHORT
                                   | s::SLONG     | s::ULONG
                                   | s::SLONGLONG | s::ULONGLONG
                                   | s::FLOAT     | s::DOUBLE
                                   | s::VOIDPTR
                                   ),

                                     a
                                   )
                                =>
                                a;

                            type (s::STRUCT c_name, a as (f, struct_names, u, e))
                                 =>
                                 case (@? (structs, c_name))
                                     THE _ =>  a;
                                     NULL  =>  (f, insert_name (c_name, struct_names), u, e);
                                 esac;

                            type (s::UNION c_name, a as (f, s, union_names, e))
                                 =>
                                 case (@? (unions, c_name))
                                     THE _ =>  a;
                                     NULL  =>  (f, s, insert_name (c_name, union_names), e);
                                 esac;

                            type (s::ENUM (c_name, anon), a as (f, s, u, enum_names))
                                 =>
                                 if (collect_enums and anon)
                                     a;
                                 else
                                     case (@? (enums, c_name))
                                         THE _ =>  a;
                                         NULL  =>  (f, s, u, insert_name (c_name, enum_names));
                                     esac;
                                 fi;

                            type ((s::PTR (_, t) | s::ARR { t, ... } ), a)
                                 =>
                                 type (t, a);

                            type (s::FPTR (cft as { args, result } ), a)
                                 =>
                                 {   a' = fold_forward type a args;

                                     a'' =  case result
                                                NULL  =>   a';
                                                THE t =>   type (t, a');
                                            esac;

                                     my (fn_ptrs, s, u, e) = a'';

                                     cfth = hash_cft cft;

                                     i = im::vals_count fn_ptrs;

                                     if  (im::contains_key (fn_ptrs, cfth))   (fn_ptrs, s, u, e);
                                     else                                     (im::set (fn_ptrs, cfth, (cft, i)), s, u, e);
                                     fi;
                                 };

                            type (s::UNIMPLEMENTED _, a)
                                 =>
                                 a;
                        end;

                        fun fs (s::OFIELD { spec => (_, t), ... }, a) =>   type (t, a); # Recurse on type of ordinary field.
                            fs (_,                                 a) =>   a;                   # Bitfields are ignorable.
                        end;

                        fun do_field ( { name, spec }, a)
                            = 
                            fs (spec, a);

                        fun do_struct ( { src, c_name, size, anon, fields, exclude }, a)
                            =
                            fold_forward do_field a fields;

                        fun do_union ( { src, c_name, size, anon, all, exclude }, a)
                            =
                            fold_forward do_field a all;

                        fun do_global_type     ( { src, c_name, spec            }, a) =   type (spec, a);
                        fun do_global_variable ( { src, c_name, spec => (_, t)  }, a) =   type (t, a);
                        fun do_global_function ( { src, c_name, spec, arg_names }, a) =   type (s::FPTR spec, a);

                        # Initialize result state to empty:
                        #
                        result = ( im::empty,           # fptr_types
                                   ss::empty,           # incomplete_structs
                                   ss::empty,           # incomplete_unions
                                   ss::empty            # incomplete_enums
                                 );


                        # Process 'structs' list into result:
                        #
                        result = sm::fold_forward
                                        do_struct               # Fn to apply to list elements.
                                        result                  # Where to save results.
                                        structs;                # List to process.


                        # Process 'unions' list into result:
                        #
                        result = sm::fold_forward
                                        do_union                # Fn to apply to list elements.
                                        result                  # Where to save results.
                                        unions;                 # List to process.


                        # Process 'global_types' list into result:
                        #
                        result = fold_forward
                                        do_global_type          # Fn to apply to list elements.
                                        result                  # Where to save results.
                                        global_types;           # List to process.


                        # Process 'global_variables' list into result:
                        #
                        result = fold_forward
                                        do_global_variable      # Fn to apply to list elements.
                                        result                  # Where to save results.
                                        global_variables;       # List to process.


                        # Process 'global_functions' list into result:
                        #
                        result = fold_forward
                                        do_global_function      # Fn to apply to list elements.
                                        result                  # Where to save results.
                                        global_functions;       # List to process.

                        result;
                    };

                fun is_incomplete_struct t =   ss::member (incomplete_structs, t);
                fun is_incomplete_union  t =   ss::member (incomplete_unions,  t);

                fun rw_ro  s::RW =>   typ "Rw";
                    rw_ro  s::RO =>   typ "Ro";
                end;


                # Construct a type corresponding to a dimension
                # of an array -- this is an integer val encoded
                # as a phantom type expression, a decimal digit
                # at a time:  
                #
                fun dim_ty  0  =>  typ "Dec";
                    dim_ty  n  =>  type_constructor ("Dg" + int::to_string (n % 10),
                                       [dim_ty (n / 10)]);
                end;


                # Above, with negative-array-size checking added:
                #
                dim_ty
                    =
                    \\ n
                        =
                        if (n >= 0)   dim_ty n;
                        else          raise exception DIE "negative dimension";
                        fi;

                fun suchunk'rw p sut =   type_constructor ("Su_Chunk" + p, [sut, typ "Rw"]);
                fun suchunk'ro   sut =   type_constructor ("Su_Chunk'",    [sut, typ "Ro"]);



                # "fptr" is "function pointer".
                # The 'p' (prime) arg will be either "" or "'".
                # "args" and "result" are the function i/o types.
                #
                fun witness_fptr_p p { args, result }                   # Called only from witness_type_p
                    =
                    {   # Convert 'spec' type to prettyprint form.
                        # "p_type" may mean "prettyprint type":

                        fun to_p_type (s::STRUCT t) =>   suchunk'ro (st t);
                            to_p_type (s::UNION  t) =>   suchunk'ro (un t);
                            to_p_type t             =>   witness_type' t;
                        end;


                        # Returning struct and union values in C is
                        # always an ugly hack.  We handle these cases
                        # by prepending to the argument list an additional
                        # argument pointing to where the result should be
                        # stored.  That's what the 'extra_arg_type' kludge
                        # here is about:
                        #
                        my (result_type, extra_arg_type)
                            =
                            case result
                              
                               NULL => (void, []);

                               THE (s::STRUCT t)
                                    =>
                                    {   ot =   suchunk'rw "'" (st t);

                                        (ot, [ot]);
                                    };

                               THE (s::UNION t)
                                    =>
                                    {   ot =   suchunk'rw "'" (un t);

                                        (ot, [ot]);
                                    };

                               THE t => (to_p_type t, []);
                        esac;

                        arg_type_list =   extra_arg_type   @   map  to_p_type  args;
                        domain_type   =   tuple arg_type_list;
                        function_type =   arrow (domain_type, result_type);

                        type_constructor ("Fptr" + p, [function_type]);
                    }

                also
                fun witness_type_p p (t as ( s::SCHAR     | s::UCHAR
                                           | s::SINT      | s::UINT
                                           | s::SSHORT    | s::USHORT
                                           | s::SLONG     | s::ULONG
                                           | s::SLONGLONG | s::ULONGLONG
                                           | s::FLOAT     | s::DOUBLE
                                           | s::VOIDPTR))
                        =>
                        typ (stem t);

                    witness_type_p p (s::STRUCT t)           =>  type_constructor ("Su",      [st t]);
                    witness_type_p p (s::UNION t)            =>  type_constructor ("Su",      [un t]);
                    witness_type_p p (s::ENUM ta)            =>  type_constructor ("Enum",    [en ta]);
                    witness_type_p p (s::PTR (c, t))         =>  type_constructor ("Ptr" + p, [type_constructor ("Chunk", [witness_type t, rw_ro c])]);
                    witness_type_p p (s::ARR { t, d, ... } ) =>  type_constructor ("Arr",     [witness_type t, dim_ty d]);

                    witness_type_p p (s::FPTR spec)          =>  witness_fptr_p p spec;
                    witness_type_p _ (s::UNIMPLEMENTED what) =>  unimp what;
                end 

                also
                fun witness_type t
                     =
                     witness_type_p "" t

                also
                fun witness_type' t
                     =
                     witness_type_p "'" t;

                fun topfunc_ty p ( { args, result }, arg_names)         # Called only from make_do_f's do_fsig in pprint_global_fun_pkg
                    =
                    {   # Convert type from 'spec' to prettyprint format.
                        # "p_type" may mean "unparse_type":
                        #
                        fun to_p_type (s::SCHAR | s::SINT | s::SSHORT | s::SLONG)
                                =>
                                typ "mlrep::signed::Int";               # mlrep is from   x

                            to_p_type s::SLONGLONG
                                 => 
                                 typ "mlrep::long_long_signed::Int";

                            to_p_type (s::UCHAR | s::UINT | s::USHORT | s::ULONG)
                                 =>
                                 typ "mlrep::unsigned::Unt";

                            to_p_type s::ULONGLONG
                                 =>
                                 typ "mlrep::long_long_unsigned::Unt";

                            to_p_type (s::FLOAT | s::DOUBLE)
                                 =>
                                 typ "mlrep::float::Float";

                            to_p_type (s::STRUCT t) =>  type_constructor ("Su_Chunk" + p, [st t, typ "X"]);
                            to_p_type (s::UNION  t) =>  type_constructor ("Su_Chunk" + p, [un t, typ "X"]);
                            to_p_type (s::ENUM   _) =>  typ "mlrep::signed::Int";

                            to_p_type t =>   witness_type_p p t;
                        end;

                        my (result_type, extra_arg_type, extra_arg_name)
                            =
                            case result
                              
                                NULL => (void, [], []);

                                THE (s::STRUCT t)
                                     =>
                                     {   ot =   suchunk'rw p (st t);

                                         (ot, [ot], [writeto]);
                                     };

                                THE (s::UNION t)
                                     =>
                                     {   ot =   suchunk'rw p (un t);

                                         (ot, [ot], [writeto]);
                                     };

                                THE t => (to_p_type t, [], []);
                            esac;

                        arg_type_list =   map  to_p_type  args;

                        aggreg_argty
                            =
                            case (do_arg_names, arg_names)
                              
                                (TRUE, THE arg_name_list)
                                    =>
                                    record (
                                        paired_lists::zip                       # paired_lists  is from   src/lib/std/src/paired-lists.pkg
                                            ( map
                                                 arg_id
                                                 (extra_arg_name @ arg_name_list),

                                              extra_arg_type @ arg_type_list
                                            )
                                    );

                                _   =>
                                    tuple (extra_arg_type @ arg_type_list);
                            esac;

                        arrow (aggreg_argty, result_type);
                    };

                fun rtti_ty t                                                   # "rtti" == "run-time type information"
                    =
                    type_constructor ("t::Type", [witness_type t]);

                fun chunk_ty p (type, constness)
                    =
                    type_constructor ("Chunk" + p, [witness_type type, constness]);

                fun c_ro s::RW => typ "X";      # Type variable -- match anything.
                    c_ro s::RO => typ "Ro";
                end;

                fun dim_val n
                    =
                    eapp  (build n,  evar "dim")
                    where
                        fun build 0 =>  evar "dec";
                            build n =>  eapp (build (n / 10),
                                              evar ("dg" + int::to_string (n % 10)));
                        end;
                    end;

                exception INCOMPLETE;

                stipulate
                    fun simple v
                        =
                        evar ("t::" + v);
                herein
                    fun rtti_val (t as ( s::SCHAR     | s::UCHAR
                                       | s::SINT      | s::UINT
                                       | s::SSHORT    | s::USHORT
                                       | s::SLONG     | s::ULONG
                                       | s::SLONGLONG | s::ULONGLONG
                                       | s::FLOAT     | s::DOUBLE
                                       | s::VOIDPTR
                                 )     )
                            =>
                            simple (string::to_lower (stem t));

                        rtti_val (s::STRUCT t)
                             =>
                             if (is_incomplete_struct t   )   raise exception INCOMPLETE;
                                                         else   evar (styp t);       fi;

                        rtti_val (s::UNION t)
                             =>
                             if (is_incomplete_union t    )   raise exception INCOMPLETE;
                                                         else   evar (utyp t);       fi;

                        rtti_val (s::ENUM ta)
                             =>
                             econstr (evar "t::enum",
                                      type_constructor ("t::Type", [type_constructor ("Enum", [en ta])]));

                        rtti_val (s::FPTR cft)
                             =>
                             {   cfth = hash_cft cft;

                                 case (%? (fptr_types, cfth))
                                     THE (_, i) =>   evar (fptr_rtti_struct_id_cc_type i);
                                     NULL       =>   raise exception DIE "fptr type missing";
                                 esac;
                             };

                        rtti_val (s::PTR (s::RW, t))
                             =>
                             eapp (evar "t::pointer", rtti_val t);

                        rtti_val (s::PTR (s::RO, t))
                             =>
                             eapp (evar "t::ro", eapp (evar "t::pointer", rtti_val t));

                        rtti_val (s::ARR { t, d, ... } )
                             =>
                             eapp (evar "t::arr", etuple [rtti_val t, dim_val d]);

                        rtti_val (s::UNIMPLEMENTED what)
                             =>
                             raise exception INCOMPLETE;
                    end;
                end;

                fun fptr_makecall spec
                    =
                    {   h =   hash_cft spec;

                        case (%? (fptr_types, h))
                            THE (_, i) =>  fptr_rtti_struct_id_cc_makecall  i;
                            NULL       =>  raise exception DIE "missing fptr_type (makecall)";
                        esac;
                    };



                # Open an output prettyprint stream.
                # Return the stream plus a passel of
                # functions specialized to print on it:
                #
                fun open_pp (f, src)
                    =
                    { prettyprinter => pp,
                      nl, str, sp, nsp, hvbox,
                      hbox, wrapbox, vbox, end_box,
                      ppty, unparse_expression, unparse_fun, line,
                      pprint_vdef, pprint_function_def, pprint_type_def,
                      pprint_vdecl,
                      close_pp
                    }
                    where
                        output_stream  =  out::make_plain_file_prettyprinter_output_stream_avoiding_pointless_file_rewrites  f;
                        #
                        pp  =  pp::make_plain_file_prettyprinter_avoiding_pointless_file_rewrites  output_stream;

                        fun nl ()      =  pp::newline pp;
                        fun str s      =  pp::lit pp s;

                        fun sp ()      =  pp::blank pp 1;
                        fun nsp ()     =  pp::nonbreakable_blanks pp 1;

                        fun hbox ()    =  pp::open_box (pp, pp::typ::BOX_RELATIVE { blanks => 1, tab_to => 0, tabstops_are_every => 4 },        pp::horizontal,   100   );
                        fun hvbox x    =  pp::open_box (pp, x,                                                                                  pp::normal,       100   );
                        fun wrapbox a  =  pp::open_box (pp, pp::typ::BOX_RELATIVE { blanks => a, tab_to => 0, tabstops_are_every => 4 },        pp::ragged_right, 100   );
                        fun vbox a     =  pp::open_box (pp, pp::typ::BOX_RELATIVE { blanks => a, tab_to => 0, tabstops_are_every => 4 },        pp::vertical,     100   );

                        fun end_box () =  pp::shut_box              pp;

                        fun ppty t               =  p::unparse_type        pp  t;
                        fun unparse_expression e =  p::unparse_expression  pp  e;
                        fun unparse_fun x        =  p::unparse_fun         pp  x;

                        fun line s
                            =
                            {   nl ();
                                str s;
                            };

                        fun pprint_vdef (variable, expression)                          # "pprint_vdef" == "print value definition"
                            =
                            {   nl ();
                                wrapbox 4;
                                str "/* my   */  ";
                                nsp ();
                                str variable;
                                nsp ();
                                str "=";
                                sp ();
                                unparse_expression expression;
                                str ";";
                                end_box ();
                            };

                        fun pprint_function_def (f, args, result)
                            =
                            {   nl ();
                                unparse_fun (f, args, result);
                                str ";";
                            };

                        fun pprint_decl                                 # "pprint_decl" == "print_declaration", I expect.
                                ( keyword,                              # Either "type" or "my".
                                  connector                             # "=" for "type, ":" for my".
                                )
                                ( v,                                    # variable name, as a string.
                                  t                                     # variable's type, as a p::Mltype.
                                )
                            =
                            {   nl ();
                                wrapbox 4;
                                str keyword;
                                nsp ();
                                str v;
                                nsp ();
                                str connector;
                                sp ();
                                ppty t;
                                str ";";
                                end_box ();
                            };

                        pprint_type_def  =   pprint_decl ("/* type */  ", "=");
                        pprint_vdecl =   pprint_decl ("/* my   */", ":");               # "pprint_vdecl" == "print_value_declaration", I expect.

                        fun close_pp ()
                            =
                            {   pp::close_prettyprinter       pp;
                                out::close  output_stream;
                            };

                        str do_not_edit;

                        case src
                            #
                            THE s => {   nl ();
                                         str (cat ["# [from code at ", s, "]"]);
                                     };

                            NULL => ();
                        esac;

                        line credits;
                        line comments_to;

                        nl ();
                        nl ();
                    end;



                # A function to generate files named "callop-6.pkg" etc
                # with contents like
                # 
                #     package callop_6 {
                #         
                #             callop = p::ECONSTR (
                #                          p::EVAR "raw_mem_inline_t::rawccall",
                #                          <...>
                #                      );
                #         };
                # 
                # for calling C functions of a given type <...>.
                # 
                # Return value is "callop_6::callop" or such.
                # 
                # We avoid generating duplicates by remembering
                # which packages we have already generated, and
                # simply returning a pre-existing one if possible:
                #
                get_callop
                    =
                    get
                    where
                        ncallops =   REF 0;                                     # How many have we generated so far?
                        callops  =   REF im::empty;                             # Cache of already-generated packages.

                        fun callop_sid i =   "callop_" + int::to_string i;      # 
                        fun callop_qid i =   callop_sid i + "::callop";         # 

                        fun get (lib7_args_t, e_proto, ml_result_type)
                            =
                            callop_qid i
                            where 
                                e_proto_hash =   hash_lib7type e_proto;         # Hash the function prototype.

                                i =  case (%? (*callops, e_proto_hash)) # Have we already generated an appropriate package?
                                       
                                         THE i => i;                            # Yes, just use it.

                                         NULL                                   # No, we have work to do.
                                             =>
                                             {   i    =   *ncallops;            # Package number.
                                                 sn   =   callop_sid i;                         # "sn" == "serial_number", most likely.
                                                 file =   validate_pkg_filename ("callop-" + int::to_string i);

                                                 (open_pp (file, NULL))
                                                     ->
                                                     { pprint_vdef, close_pp, str, nl, wrapbox, end_box, ... };

                                                 ncallops :=  i + 1;
                                                 callops  :=  im::set (*callops, e_proto_hash, i);

                                                 str (cat ["package ", sn]); nl ();
                                                 str "    {";      nl ();
                                                 wrapbox 8;
                                                 pprint_vdef ("callop",
                                                          econstr (evar "raw_mem_inline_t::rawccall",
                                                                   arrow (tuple [typ "one_word_unt::Unt",               # one_word_unt  is from   src/lib/std/one-word-unt.pkg
                                                                                 lib7_args_t,
                                                                                 e_proto],
                                                                          ml_result_type)));
                                                 end_box ();
                                                 nl ();
                                                 str "};";
                                                 nl ();
                                                 close_pp ();

                                                 i;
                                             };
                                     esac;
                            end;
                    end;


                # "pprint_fptr_rtti" == "prettyprint function pointer runtime type information", I think.
                #
                # Here we generate a file "fptr-rtti-6.pkg"
                # or such containing something like
                #
                #     package fptr_rtti_6 {
                #             stipulate
                #                 include package   c::dim;
                #                 include package   c_internals;
                #             herein
                #                 fun makecall <...>;
                #                 my type = <...>;
                #             end;
                #         };
                #
                fun pprint_fptr_rtti ( { args, result }, i)
                    =
                    {   package_name =   fptr_rtti_struct_id i;                         # "fptr_rtti_6" or such.

                        file =   validate_pkg_filename ("fptr-rtti-" + int::to_string i);               # OS path for "fptr-rtti-6.pkg" or such.

                        (open_pp (file, NULL))
                            ->
                            { close_pp, str, wrapbox, end_box, pprint_function_def, pprint_vdef, nl, ... };

                        #  Cproto encoding 

                        fun list t
                            =
                            type_constructor ("List", [t]);

                        real   = typ "Float";
                        char   = typ "Char";
                        one_byte_unt   = typ "one_byte_unt::Unt";                                       # one_byte_unt  is from   src/lib/std/one-byte-unt.pkg
                        tagged_int  = typ "tagged_int::Int";                            # tagged_unt    is from   src/lib/std/tagged-unt.pkg
                        tagged_unt  = typ "tagged_unt::Unt";
                        one_word_int  = typ "one_word_int::Int";                                        # one_word_unt  is from   src/lib/std/one-word-unt.pkg
                        one_word_unt  = typ "one_word_unt::Unt";
                        string = typ "String";
                        exn    = typ "Exception";



                        # See src/lib/compiler/front/semantic/types/cproto.pkg for these:
                        #
                        e_double = real;                        # The "e_" prefix is likely short for "encode_" or "encoded_"
                        e_float  = list real;
                        e_schar  = char;
                        e_uchar  = one_byte_unt;
                        e_sint   = tagged_int;
                        e_uint   = tagged_unt;
                        e_slong  = one_word_int;
                        e_ulong  = one_word_unt;
                        e_sshort = list char;
                        e_ushort = list one_byte_unt;
                        e_sllong = list one_word_int;
                        e_ullong = list one_word_unt;
                        e_ptr    = string;

                        e_nullstruct = exn;

                        fun encode s::DOUBLE    => e_double;
                            encode s::FLOAT     => e_float;

                            encode s::SCHAR     => e_schar;
                            encode s::UCHAR     => e_uchar;

                            encode s::SINT      => e_sint;
                            encode s::UINT      => e_uint;

                            encode s::SSHORT    => e_sshort;
                            encode s::USHORT    => e_ushort;

                            encode s::SLONG     => e_slong;
                            encode s::ULONG     => e_ulong;

                            encode s::SLONGLONG => e_sllong;
                            encode s::ULONGLONG => e_ullong;

                            encode (s::PTR _ | s::VOIDPTR | s::FPTR _)
                                =>
                                e_ptr;

                            encode (s::UNIMPLEMENTED what)
                                =>
                                unimp what;

                            encode (s::ARR _)
                                =>
                                raise exception DIE "unexpected rw_vector";

                            encode (s::ENUM _)
                                =>
                                e_sint;

                            encode (s::STRUCT t)
                                =>
                                case (@? (structs, t))
                                  
                                     THE s =>  encode_fields void s.fields;
                                     NULL  =>  err ["incomplete struct argument: struct ", t];
                                esac;

                            encode (s::UNION t)
                                =>
                                case (@? (unions, t))
                                    THE u =>  encode_fields e_sint u.all;
                                    NULL  =>  err ["incomplete union argument: union", t];
                                esac;
                        end 

                      also
                      fun encode_fields  dummy  fields
                           =
                           {   fun f0 (s::ARR { t, d => 0, ... }, a) =>  a;
                                   f0 (s::ARR { t, d => 1, ... }, a) =>  f0 (t, a);

                                   f0 (s::ARR { t, d, esz }, a)
                                       =>
                                       f0 (t, f0 (s::ARR { t, d => d - 1, esz }, a));

                                   f0 (t, a)
                                       =>
                                       encode t ! a;
                               end;

                               fun f ( { spec => s::OFIELD { spec, ... }, name }, a)
                                       =>
                                       f0 (#2 spec, a);

                                   f (_, a)
                                        =>
                                        a;
                               end;

                               fel =   fold_backward f [] fields;

                               case fel
                                   []  =>   e_nullstruct;
                                   fel =>   tuple (dummy ! fel);
                               esac;
                           };

                        e_arg =   tuple (void ! map encode args);

                        e_result =   case result      NULL  =>  void;
                                                      THE t =>  encode t;
                                     esac;

                        e_proto =   type_constructor ("List", [arrow (e_arg, e_result)]);

                        # Generating the call operation 

                        # A low-level type used to communicate a value
                        # to the low-level call operation
                        #
                        fun mlty (t as ( s::SCHAR     | s::UCHAR
                                       | s::SINT      | s::UINT
                                       | s::SSHORT    | s::USHORT
                                       | s::SLONG     | s::ULONG
                                       | s::SLONGLONG | s::ULONGLONG
                                       | s::FLOAT     | s::DOUBLE
                                 )     )
                                =>
                                typ ("c_memory::cc_" + stem t);

                            mlty (s::VOIDPTR | s::PTR _ | s::FPTR _ | s::STRUCT _ | s::UNION _)
                                 =>
                                 typ "c_memory::cc_addr";                       # c_memory      is from   x

                            mlty (s::ENUM _)             => typ "c_memory::cc_sint";
                            mlty (s::UNIMPLEMENTED what) => unimp what;
                            mlty (s::ARR _)              => raise exception DIE "unexpected type";
                        end;

                        fun wrap (e, n)
                            =
                            eapp (evar ("c_memory::wrap_" + n),
                                  eapp (evar ("convert::ml_" + n), e));         # convert       is from   x

                        fun vwrap e =   eapp (evar "c_memory::wrap_addr", eapp (evar "reveal",  e));
                        fun fwrap e =   eapp (evar "c_memory::wrap_addr", eapp (evar "freveal", e));
                        fun pwrap e =   eapp (evar "c_memory::wrap_addr", eapp (evar "reveal", eapp (evar "ptr::inject'", e)));

                        fun suwrap e
                            =
                            pwrap (eapp (evar "ptr::enref'", e));                               # ptr   is from   x

                        fun ewrap e
                            =
                            eapp (evar "c_memory::wrap_sint",
                                            eapp (evar "convert::c2i_enum", e));


                        # This code is for passing structures in pieces
                        # (member-by-member). We don't use this; rather we
                        # provide a pointer to the beginning of the struct.
                        #
                        fun arglist ([], _)
                                =>
                                ([], []);

                            arglist (h ! tl, i)
                                 =>
                                 {   p =   evar ("x" + int::to_string i);

                                     my  (ta, ea)
                                         =
                                         arglist (tl, i + 1);

                                     fun sel e
                                         =
                                         (   mlty h ! ta,
                                             e ! ea
                                         );

                                     case h
                                       
                                         (s::STRUCT _ | s::UNION _) =>  sel (suwrap p);
                                         (s::ENUM _)                =>  sel (ewrap p);

                                         ( s::SCHAR     | s::UCHAR
                                         | s::SINT      | s::UINT
                                         | s::SSHORT    | s::USHORT
                                         | s::SLONG     | s::ULONG
                                         | s::SLONGLONG | s::ULONGLONG
                                         | s::FLOAT     | s::DOUBLE
                                         )   =>
                                             sel (wrap (p, stem h));

                                         s::VOIDPTR =>  sel (vwrap p);
                                         s::PTR _   =>  sel (pwrap p);
                                         s::FPTR _  =>  sel (fwrap p);

                                         s::UNIMPLEMENTED what =>  unimp_arg what;
                                         s::ARR _              =>  raise exception DIE "unexpected rw_vector argument";
                                     esac;
                                 };
                        end;

                        my  ( ml_result_type,
                              extra_arg_v,
                              extra_arg_e,
                              extra_lib7_arg_t,
                              res_wrap
                            )
                            =
                            case result

                                NULL
                                    =>
                                    (void, [], [], [], \\ r =  r);

                                THE (s::STRUCT _ | s::UNION _)
                                    =>
                                    ( void,
                                      [evar "x0"],
                                      [suwrap (evar "x0")],
                                      [typ "c_memory::cc_addr"],
                                      \\ r =  eseq (r, evar "x0")
                                    );

                                THE t
                                    =>
                                    {   fun unwrap n r
                                            =
                                            eapp (evar ("convert::c_" + n),
                                                  eapp (evar ("c_memory::unwrap_" + n), r));

                                        fun punwrap cast r
                                            =
                                            eapp (evar cast,
                                                  eapp (evar "c_memory::unwrap_addr", r));

                                        fun eunwrap r
                                            =
                                            eapp (evar "convert::i2c_enum",
                                                  eapp (evar "c_memory::unwrap_sint", r));

                                        res_wrap
                                            =
                                            case t

                                                ( s::SCHAR     | s::UCHAR
                                                | s::SINT      | s::UINT
                                                | s::SSHORT    | s::USHORT
                                                | s::SLONG     | s::ULONG
                                                | s::SLONGLONG | s::ULONGLONG
                                                | s::FLOAT     | s::DOUBLE
                                                )   =>
                                                    unwrap (stem t);

                                                s::VOIDPTR =>  punwrap "vcast";
                                                s::FPTR _  =>  punwrap "fcast";
                                                s::PTR _   =>  punwrap "pcast";
                                                s::ENUM _  =>  eunwrap;

                                                s::UNIMPLEMENTED what
                                                    =>
                                                    unimp_res what;

                                                (s::STRUCT _ | s::UNION _ | s::ARR _)
                                                     =>
                                                     raise exception DIE "unexpected result type";
                                            esac;

                                        (mlty t, [], [], [], res_wrap);
                                    };
                            esac;

                        my  (lib7_args_tl, args_el)
                            =
                            arglist (args, 1);

                        lib7_args_t
                            =
                            tuple  (extra_lib7_arg_t @ lib7_args_tl);

                        arg_vl                                                  # "arg_vl" == "arg_variable_list" ?
                            =
                            reverse (
                                #1 (fold_forward
                                        (\\ (_, (a, i))
                                             =
                                             (  evar ("x"  +  int::to_string i)   !   a,
                                                i + 1
                                             )
                                        )
                                        ([], 1)
                                        args
                                   )
                            );

                        arg_e =   etuple  (extra_arg_e @ args_el);

                        callop_n =   get_callop (lib7_args_t, e_proto, ml_result_type);

                        str "stipulate"; nl ();
                        str "    include package   c::dim;"; nl ();
                        str "    include package   c_internals;"; nl ();
                        str "herein";  nl ();

                        str (cat ["package ", package_name, " {"]);
                        wrapbox 4;
                        pprint_function_def ("makecall",
                                 [evar "a", etuple (extra_arg_v @ arg_vl)],
                                 res_wrap (eapp (evar callop_n,
                                                 etuple [evar "a", arg_e,
                                                         evar "NIL"])));
                        pprint_vdef ("rtti",
                                 econstr (eapp (evar "make_fptr_type",
                                                evar "makecall"),
                                          rtti_ty (s::FPTR { args,
                                                            result } )));
                        end_box ();
                        nl ();
                        str "};";
                        nl ();
                        str "end;";
                        nl ();
                        close_pp ();
                    };                                  # fun pprint_fptr_rtti



                # "pprint_sue_pkg" == "prettyprint struct/union/enum package"
                #
                # Here we generate a file like
                #     incomplete-struct-foo.pkg
                #      incomplete-union-foo.pkg
                #       incomplete-enum-foo.pkg
                # or such containing something like
                #
                # stipulate
                #     package [SUE]foo {
                #             with
                #                 include package   tag;
                #             do
                #                 Tag = <...>;
                #             end;
                #             size = <...>;             # Optional.
                #             type  = <...>;            # Optional.
                #         };
                # herein
                #     package [SUE]T_foo
                #         =
                #         [SUE]foo;
                # end;

                Sue_Szinfo
                  = RTTI_INCOMPLETE                     #  Generate no RTTI 
                  | RTTI_STRUCT_OR_UNION  Unt           #  Generate struct/union RTTI 
                  | RTTI_ENUM                           #  Generate enum RTTI 
                  ;

                fun pprint_sue_pkg (
                      src,
                      c_name,
                      anon,
                      tinfo,
                      kind,             # "struct"/"union"/"enum"
                      kkkind            # "Struct"/"Union"/"Enum"
                    )
                    =
                    {   file =   validate_pkg_filename (cat ["incomplete-", kind, c_name]);

                        (open_pp (file, src))
                            ->
                            { str, close_pp, nl, wrapbox, end_box, vbox, pprint_type_def, pprint_vdef, ... };


                        # C uses name equivalence:  Two types are the same if
                        # they are declared with the same name.  To model this
                        # in Mythryl, which uses structural equivalence, we
                        # defined types which are struct/union names spelled
                        # out.  For example, C struct name "foo" becomes
                        # the  src/lib/c-glue-lib/internals/tag.pkg type
                        #    Tyf Tyo Tyo 
                        # where the trailing letters of "Tyf Tyo Tyo" spell out "foo".
                        #
                        fun cname_to_tagtype  cname
                            =
                            eat_charlist (string::explode cname)
                            where
                                fun eat_charlist []
                                        =>
                                        typ ("Type_" + kind);

                                    eat_charlist (h ! tl)
                                        =>
                                        # 'f' becomes 'Tyf' but
                                        # 'F' becomes 'Ty_F' to fit
                                        # within our capitalization conventions:
                                        if   (char::is_upper h)   type_constructor ("Ty_" + string::from_char h, [eat_charlist tl]);
                                        else                      type_constructor ("Ty"  + string::from_char h, [eat_charlist tl]);
                                        fi;
                                end;
                            end;

                        my  (utildef, tag_t)
                            =
                            if anon
                                ( "package x   :>   api Type; end   { Type = Void; }",
                                  typ "x::Type"
                                );
                            else
                                ( "include package   tag;\t\t# String-to-type encoding utility.",
                                  cname_to_tagtype c_name
                                );
                            fi;

                        str "local";
                        wrapbox 4;
                        nl ();
                        str (cat ["package ", sue_package_name kind c_name]);  nl ();
                        str "   {";    nl ();
                        wrapbox 4;
                        nl (); str "stipulate";
                        vbox 4;
                        nl (); str utildef;
                        end_box ();
                        nl (); str "herein";
                        vbox 4;
                        pprint_type_def ("Tag", tag_t);
                        end_box ();
                        nl (); str "end;";

                        case tinfo
                          
                            RTTI_INCOMPLETE => ();
                            RTTI_ENUM       => ();

                            RTTI_STRUCT_OR_UNION size
                                =>
                                {   pprint_vdef ("size",
                                          econstr (eapp (evar "c_internals::make_su_size", eword size),         # c_internals   is from   x
                                                   type_constructor ("c::s::size",
                                                        [type_constructor ("c::su", [typ "tag"])])));
                                    pprint_vdef ("rtti",
                                          eapp (evar "c_internals::make_su_type", evar "size"));
                                };
                        esac;

                        end_box (); nl ();
                        str "};";
                        end_box (); nl ();
                        str "herein";
                        wrapbox 4;      nl ();
                        str (cat ["package ", incomplete_sue_package_name kind c_name, " = ", sue_package_name kind c_name]);
                        end_box (); nl ();
                        str "end;"; nl ();
                        close_pp ();
                    };                                  # fun pprint_sue_pkg

                stipulate

                    p =   pprint_sue_pkg;

                herein

                    fun pprint_struct_pkg { src, c_name, anon, size,  fields, exclude }
                        =
                        p (THE src, c_name, anon, RTTI_STRUCT_OR_UNION size, "struct", "Struct");

                    fun pprint_union_pkg { src, c_name, anon, size,  all,    exclude }
                        =
                        p (THE src, c_name, anon, RTTI_STRUCT_OR_UNION size, "union", "Union");

                    fun pprint_enum_pkg { src, c_name, anon, descr, spec,   exclude }
                        =
                        p (THE src, c_name, anon, RTTI_ENUM,                 "enum",  "Enum");
                end;


                # Generate sourcefiles for incomplete
                # struct/union/enum definitions:
                #
                fun pprint_incomplete_sue_pkg (c_name, kind, kkkind)
                    =
                    {   pprint_sue_pkg (NULL, c_name, FALSE, RTTI_INCOMPLETE, kind, kkkind);

                        exported_packages :=   ("package " + incomplete_sue_package_name kind c_name)
                                               !
                                               *exported_packages;
                    };

                fun pprint_incomplete_struct_pkg c_name =  pprint_incomplete_sue_pkg (c_name, "struct", "Struct");
                fun pprint_incomplete_union_pkg  c_name =  pprint_incomplete_sue_pkg (c_name, "union",  "Union" );
                fun pprint_incomplete_enum_pkg   c_name =  pprint_incomplete_sue_pkg (c_name, "enum",   "Enum"  );



                # Write a file struct-foo-accessors.pkg or
                #               union-foo-accessors.pkg
                # containing all the Mythryl accessors
                # for a given C struct/union.
                # 
                fun pprint_su_pkg (
                        src,
                        c_name,
                        fields,
                        kind,           # "struct"/"union"
                        kkkind          # "Struct"/"Union"
                    )
                    =
                    {   file =   validate_pkg_filename (cat [kind, "-", c_name, "-accessors"]);

                        (open_pp (file, THE src))
                            ->
                            { close_pp, wrapbox, end_box, str, nl, line, pprint_type_def, pprint_vdef, pprint_function_def, ... };

                        fun rw_ro s::RW => "rw";
                            rw_ro s::RO => "ro";
                        end;

                        fun pprint_field_type { name, spec => s::OFIELD { spec => (c, t),
                                                                   synthetic => FALSE,
                                                                   offset } }
                                =>
                                pprint_type_def (fieldtype_id name, witness_type t);

                            pprint_field_type _
                                =>
                                ();
                        end;

                        fun pprint_field_rtti {
                                    name,
                                    spec => s::OFIELD {
                                               spec => (c, t),
                                               synthetic => FALSE,
                                               offset
                                           }
                                }
                                =>
                                pprint_vdef (fieldrtti_id name,
                                         econstr (rtti_val t,
                                                  type_constructor ("t::type", [typ (fieldtype_id name)])));
                            pprint_field_rtti _
                                =>
                                ();
                        end;

                        fun arg_x p                     # p (== "prime") is either "'" or "".
                            =
                            econstr (
                                evar "x",
                                type_constructor (
                                    "Su_Chunk" + p,
                                    [typ "tag", typ "X"]
                                )
                            );



                        fun pprint_bitfield_accessor (name, p, sign, { offset, constness, bits, shift } )
                            =
                            {   maker
                                    =
                                    cat ["make_", rw_ro constness, "_", sign, "bf", p];

                                pprint_function_def (
                                    field_id (name, p),
                                    [arg_x p],
                                    eapp ( eapp (evar maker,
                                                etuple [eint offset,
                                                        eword bits,
                                                        eword shift]),
                                           evar "x"
                                         )
                                );
                            };

                        fun pprint_field_acc' { name, spec => s::OFIELD x }
                                =>
                                {   x ->  { synthetic, spec => (c, t), offset, ... };

                                    if (not synthetic)

                                        pprint_function_def

                                            (field_id (name, "'"),

                                            [arg_x "'"],

                                            econstr
                                              (
                                                eapp (evar "make_field'",
                                                           etuple [eint offset,
                                                                   evar "x"]),

                                                type_constructor ("chunk'",
                                                     [typ (fieldtype_id name),
                                                      c_ro c]))
                                              );
                                    fi;
                                };

                            pprint_field_acc' { name, spec => s::SIGNED_BITFIELD bitfield }
                                =>
                                pprint_bitfield_accessor (name, "'", "s", bitfield);    # "s" for "signed" I'd guess.

                            pprint_field_acc' { name, spec => s::UNSIGNED_BITFIELD bitfield }
                                =>
                                pprint_bitfield_accessor (name, "'", "u", bitfield);    # "u" for "unsigned" I'd guess.
                        end;



                        # "pprint_field_acc" == "unparse_field_accessor", maybe.
                        #
                        fun pprint_field_acc { name, spec => s::OFIELD { offset,
                                                                   spec => (c, t),
                                                                   synthetic }
                                         }
                                =>
                                if (not synthetic)

                                    maker =   cat ["make_", rw_ro c, "_field"];

                                    rttival =   evar (fieldrtti_id name);

                                    pprint_function_def (field_id (name, ""),
                                             [arg_x ""],
                                             eapp (evar maker,
                                                   etuple [rttival,
                                                           eint offset,
                                                           evar "x"]));

                                fi;

                            pprint_field_acc { name, spec => s::SIGNED_BITFIELD bitfield }
                                =>
                                pprint_bitfield_accessor (name, "", "s", bitfield);             # "s" for "signed" I'd guess.

                            pprint_field_acc { name, spec => s::UNSIGNED_BITFIELD bitfield }
                                =>
                                pprint_bitfield_accessor (name, "", "u", bitfield);             # "u" for "unsigned" I'd guess.
                        end;

                        su_package_name
                            =
                            "package " + sue_package_name kkkind c_name;

                        fun pprint_one_field f
                            =
                            {   pprint_field_type f;

                                inc =  {   pprint_field_rtti f;
                                           FALSE;
                                       }
                                       except
                                           INCOMPLETE =  TRUE;

                                if (do_light or      inc)   pprint_field_acc' f;   fi;
                                if (do_heavy and not inc)   pprint_field_acc  f;   fi;
                            };

                        str "stipulate"; nl ();
                        str "   include package   c::dim;"; nl ();
                        str "   include package   c_internals;"; nl ();
                        str "herein"; nl ();
                        str (su_package_name + " {");
                        wrapbox 4;
                        nl (); str ("include package " + incomplete_sue_package_name kind c_name);
                        apply pprint_one_field fields;
                        end_box ();
                        nl (); str "};";
                        nl (); str "end;";
                        nl (); close_pp ();

                        exported_packages := su_package_name ! *exported_packages;
                    };                                          # fun pprint_su_pkg

                fun pprint_struct_accessors_pkg { src, c_name, anon, size,  fields, exclude } =  pprint_su_pkg (src, c_name, fields, "struct", "Struct");
                fun pprint_union_accessors_pkg  { src, c_name, anon, size,  all,    exclude } =  pprint_su_pkg (src, c_name, all,    "union",  "Union");



                # Write a file enum-foo-accessors.pkg containing 
                # all the Mythryl accessors for a given C enum.
                #
                fun pprint_enum_accessors_pkg { src, c_name, anon, descr, spec,   exclude }
                    =
                    {   file =   validate_pkg_filename ("enum-" + c_name + "-accessors");

                        my  { close_pp, str, wrapbox, end_box, nl, line, sp,
                              pprint_function_def, pprint_vdef, pprint_type_def, ...
                            }
                            =
                            open_pp (file, THE src);

                        estruct =   "package " + estruct' (c_name, anon);

                        fun no_duplicate_values ()
                            =
                            loop (spec, lis::empty)
                            where
                                fun loop ([], _) => TRUE;

                                    loop ( { name, spec } ! l, s)
                                        =>
                                        if  (lis::member (s, spec))

                                            warn (cat ["enum ", descr,
                                                       " has duplicate values;\
                                                       \ using sint,\
                                                       \ not generating constructors\n"]);
                                            FALSE;

                                        else

                                            loop (l, lis::add (s, spec));
                                        fi;
                                end;
                            end;

                        dodt =   enumcons and no_duplicate_values ();

                        fun dt_lib7rep ()
                            =
                            {   fun pcl ()
                                    =
                                    {   fun loop (_, [])
                                                =>
                                                ();

                                            loop (c, { name, spec } ! l)
                                                =>
                                                {   str (c + enum_id name);
                                                    nextround l;
                                                };
                                        end 

                                        also
                                        fun nextround [] =>  ();
                                            nextround l  =>  {   sp ();   loop ("| ", l);  };
                                        end;

                                        wrapbox 2; nl ();
                                        loop ("  ", spec);
                                        end_box ();
                                    };

                                fun pfl (fname, arg, result, fini)
                                    =
                                    {   fun loop (_, [])
                                                =>
                                                ();

                                            loop (pfx, v ! l)
                                                =>
                                                {   line (cat [pfx, " ", arg v, " => ", result v]);
                                                    loop ("  |", l);
                                                };
                                        end;

                                        line (cat ["fun ", fname, " x ="]);
                                        wrapbox 4;
                                        line ("case x of");
                                        loop ("   ", spec);
                                        fini ();
                                        end_box ();
                                    };


                                fun cstr { name, spec }
                                    =
                                    enum_id name;


                                fun vstr { name, spec }
                                    =
                                    large_int::to_string spec + " : mlrep::signed::Int";

                                line "enum mlrep =";
                                pcl ();
                                pfl ("m2i", cstr, vstr, \\ () = ());

                                pfl (
                                    "i2m",
                                    vstr,
                                    cstr,
                                    \\ () =  line "  | _ => raise exception exceptions::DOMAIN"         # exceptions    is from   src/lib/std/exceptions.pkg
                                );
                            };                          # fun dt_lib7rep ()

                        fun int_lib7rep ()
                            =
                            {   fun v { name, spec }
                                    =
                                    pprint_vdef (enum_id name, econstr (elint spec, typ "mlrep"));

                                mlx =   econstr (evar "x", typ "mlrep");
                                ix  =   econstr (evar "x", typ "mlrep::signed::Int");

                                pprint_type_def ("Mlrep", typ "mlrep::signed::Int");
                                apply v spec;
                                pprint_function_def ("m2i", [mlx], ix);
                                pprint_function_def ("i2m", [ix], mlx);
                            };

                        fun getset p
                            =
                            {   fun constr c
                                    =
                                    type_constructor ("enum_chunk" + p, [typ "tag", typ c]);

                                pprint_function_def ("get" + p,
                                         [econstr (evar "x", constr "'c")],
                                         eapp (evar "i2m",
                                               eapp (evar ("get::enum" + p), evar "x")));

                                pprint_function_def ("set" + p,
                                         [etuple [econstr (evar "x", constr "rw"), evar "v"]],
                                         eapp (evar ("set::enum" + p),
                                               etuple [evar "x", eapp (evar "m2i", evar "v")]));
                            };


                        str "stipulate include package   c; herein";
                        line (estruct + " {");
                        wrapbox 4;
                        line ("include package " + incomplete_sue_package_name "enum" c_name);

                        if dodt   dt_lib7rep ();
                        else     int_lib7rep ();
                        fi;

                        pprint_function_def ("c", [evar "x"],
                                 econstr (eapp (evar "convert::i2c_enum",
                                                eapp (evar "m2i", evar "x")),
                                          type_constructor ("enum", [typ "tag"])));

                        pprint_function_def ("ml", [econstr (evar "x", type_constructor ("enum", [typ "tag"]))],
                                 eapp (evar "i2m",
                                       eapp (evar "convert::c2i_enum", evar "x")));

                        if  do_light    getset "'";   fi;
                        if  do_heavy    getset "";    fi;

                        end_box ();
                        line "};";
                        line "end;   # local";
                        nl ();
                        close_pp ();

                        exported_packages :=   estruct ! *exported_packages;
                    };                                          # fun pprint_enum_accessors_pkg



                # Write a file global-type-foo.pkg
                # for a global C type.
                #
                #
                fun pprint_global_type_pkg { src, c_name, spec }
                    =
                    {   rttiv_opt =   THE (rtti_val spec)
                                      except
                                          INCOMPLETE = NULL;

                        file =   validate_pkg_filename ("global-type-" + c_name);

                        (open_pp (file, THE src))
                            ->
                            { close_pp, wrapbox, end_box, str, nl, pprint_type_def, pprint_vdef, ...  };

                        package_name_for_c_type
                            =
                            "package "   +   package_name_for_c_type  c_name;

                        str "stipulate";     nl ();
                        str "    include package   c::dim;";  nl ();
                        str "    include package   c;";       nl ();
                        str "herein";               nl ();
                        str (package_name_for_c_type + " {");
                        wrapbox 4;
                        pprint_type_def ("Type", witness_type spec);

                        null_or::apply
                            (\\ rttiv
                                 =
                                 pprint_vdef (
                                     "rtti",
                                     econstr (
                                         rttiv,
                                         type_constructor ("t::type", [typ "t"])
                                     )
                                 )
                            )
                            rttiv_opt;

                        end_box ();
                        nl ();  str "};";
                        nl ();  str "end;";
                        nl ();
                        close_pp ();
                        exported_packages :=   package_name_for_c_type ! *exported_packages;
                    };                                                          # fun pprint_global_type_pkg



                # Write a file global-var-foo.pkg containing the
                # Mythryl interface to a C global variable 'foo'.
                #
                # For a global variable "int foo;" this will look like:
                #
                #         package global_var_foo {
                #                 with
                #                     include package   c::dim;
                #                     include package   c_internals;
                #
                #                     /* my   */   handle = int1_handle::lib_handle "foo";
                #                 do
                #                     /* type */   Type = Sint;
                #
                #                     /* my   */   rtti = t::Sint : t::Type Type;
                #
                #                     fun chunk' () =   make_chunk' (handle ()) : Chunk' (Type,  Rw);
                #                     fun chunk () =   heavy::chunk rtti (chunk' ());
                #                 end;
                #             };
                #
                fun pprint_global_var_pkg {
                        src,                    # "foo.h:4596.16-25" or such -- source file region defining var.
                        c_name,                 # "foo" or such: Variable name from .h file.
                        spec => (var_constness, var_type)
                    }
                    = 
                    {   file =   validate_pkg_filename ("global-var-" + c_name);

                        (open_pp (file, THE src))
                            ->
                            { close_pp, str, nl, wrapbox, vbox, end_box, pprint_function_def, pprint_vdef, pprint_type_def, ... };

                        fun do_it ()
                            =
                            {   rwo =   typ  case var_constness
                                                  
                                                    s::RW =>  "Rw";
                                                    s::RO =>  "Ro";
                                               esac;

# THIS IS THE CENTER OF THE UNIVERSE :)
                                pprint_type_def ("Type",  witness_type  var_type);

                                nl ();

                                incomplete
                                    =
                                    {   pprint_vdef (
                                              "rtti",
                                              econstr (
                                                  rtti_val  var_type,
                                                  type_constructor ("t::Type", [typ "Type"])
                                              )
                                          );

                                          FALSE;
                                    }
                                    except
                                        INCOMPLETE = TRUE;

                                nl ();

                                chunk'
                                    =
                                    econstr (eapp (evar "make_chunk'", eapp (evar "handle", eunit)),
                                             type_constructor ("Chunk'", [typ "Type", rwo]));

                                do_light =   do_light or incomplete;

                                if do_light

                                    pprint_function_def ("chunk'", [eunit], chunk');
                                fi;


                                if (do_heavy and not incomplete)

                                    pprint_function_def (
                                        "chunk",
                                        [eunit],

                                        eapp (
                                            eapp  (evar "heavy::chunk",  evar "rtti"),                  # heavy is from   x

                                            do_light  ??  eapp (evar "chunk'", eunit)
                                                      ::  chunk'
                                        )
                                    );
                                fi;
                            };                  # fun do_it

                        package_name_for_c_global_var
                            =
                            "package "   +   package_name_for_c_global_var  c_name;

                        str package_name_for_c_global_var;
                        wrapbox 4;  nl ();
                        str "{";    nl ();
                        wrapbox 4;  nl ();
                        str "stipulate";
                        vbox 4;     nl ();
                        str "include package   c::dim;";  nl ();
                        str "include package   c_internals;";  nl ();
                        pprint_vdef ("handle", eapp (evar library_handle, estring c_name));
                        end_box (); nl (); 
                        str "herein";
                        vbox 4;

                        do_it ();

                        end_box (); nl (); 
                        str "end;";
                        end_box (); nl (); 
                        str "};"; nl ();
                        end_box (); nl (); 
                        close_pp ();

                        exported_packages
                            :=
                            package_name_for_c_global_var
                            !
                            *exported_packages;
                    };                                                  # fun pprint_global_var_pkg



                # Write a file global-function-foo.pkg containing
                # a global function declaration.
                #
                fun pprint_global_fun_pkg x
                    = 
                    {   x ->  { src, c_name, spec => spec as { args, result }, arg_names };

                        file =   validate_pkg_filename ("global-function-" + c_name);

                        (open_pp (file, THE src))
                            ->
                            { close_pp,
                              str,
                              nl,
                              pprint_function_def,
                              wrapbox,
                              end_box,
                              pprint_vdef,
                              pprint_vdecl,
                              ...
                            };

                        fun make_do_f  is_light
                            =
                            {   ml_vars
                                    =
                                    reverse (
                                        #1 (fold_forward
                                               (\\ (_, (l, i))
                                                   =
                                                   (evar 
                                                       ("x" + int::to_string i) ! l,
                                                       i + 1
                                                   )
                                               )
                                               ([], 1)
                                               args
                                           )
                                    );

                                fun app0 (what, e)
                                    =
                                    if is_light      e;
                                    else             eapp (evar what, e);
                                    fi;

                                fun light (what, e)
                                    =
                                    app0 ("light::" + what, e);                 # light is from   x

                                fun heavy (what, t, e)
                                    =
                                    is_light ??  e
                                             ::  eapp (eapp (evar ("heavy::" + what), rtti_val t), e);

                                fun one_arg (e, t as ( s::SCHAR     | s::UCHAR
                                                     | s::SINT      | s::UINT
                                                     | s::SSHORT    | s::USHORT
                                                     | s::SLONG     | s::ULONG
                                                     | s::SLONGLONG | s::ULONGLONG
                                                     | s::FLOAT     | s::DOUBLE
                                            )        )
                                        =>
                                        eapp (evar ("convert::c_" + stem t), e);

                                    one_arg (e, (s::STRUCT _ | s::UNION _))
                                         =>
                                         eapp (evar "ro'", light ("chunk", e));

                                    one_arg (e, s::ENUM ta) =>  eapp (evar "convert::i2c_enum", e);
                                    one_arg (e, s::PTR  _)  =>  light ("ptr", e);
                                    one_arg (e, s::FPTR _)  =>  light ("fptr", e);
                                    one_arg (e, s::VOIDPTR) =>  e;

                                    one_arg (e, s::UNIMPLEMENTED what) =>  unimp_arg what;
                                    one_arg (e, s::ARR _)              =>  raise exception DIE "rw_vector argument type";
                                end;

                                c_exps =   paired_lists::map  one_arg  (ml_vars, args);

                                my  (ml_vars, c_exps, extra_arg_name)
                                    =
                                    case result
                                      
                                        THE (s::STRUCT _ | s::UNION _)
                                            =>
                                            (   evar "x0" ! ml_vars,
                                                light ("chunk", evar "x0") ! c_exps,
                                                [ writeto ]
                                            );

                                        _ => (ml_vars, c_exps, []);
                                    esac;

                                call =   eapp (evar "call",
                                                 etuple [eapp (evar "fptr", eunit),
                                                         etuple c_exps]);

                                ml_res
                                    =
                                    case result
                                      
                                        THE (t as (s::SCHAR | s::UCHAR | s::SINT | s::UINT |
                                                   s::SSHORT | s::USHORT | s::SLONG | s::ULONG |
                                                   s::SLONGLONG | s::ULONGLONG |
                                                   s::FLOAT | s::DOUBLE))
                                            =>
                                            eapp (evar ("convert::ml_" + stem t), call);

                                        THE (t as (s::STRUCT _ | s::UNION _))
                                             =>
                                             heavy ("chunk", t, call);

                                        THE (s::ENUM ta)     => eapp (evar "convert::c2i_enum", call);
                                        THE (t as s::PTR _)  => heavy ("ptr", t, call);
                                        THE (t as s::FPTR _) => heavy ("fptr", t, call);
                                        THE (s::ARR _)       => raise exception DIE "rw_vector result type";

                                        THE (s::UNIMPLEMENTED what) => unimp_res what;
                                        (NULL | THE s::VOIDPTR)     => call;
                                    esac;

                                argspat
                                    =
                                    case (do_arg_names, arg_names)
                                      
                                        (TRUE, THE arg_name_list)
                                            =>
                                            erecord (paired_lists::zip ( map arg_id (extra_arg_name @ arg_name_list),
                                                                      ml_vars
                                                                    )
                                                    );

                                        _   =>
                                            etuple  ml_vars;
                                    esac;

                                \\ ()
                                    =
                                    pprint_function_def (
                                      is_light ?? "f'" :: "f",
                                      [argspat],
                                      ml_res
                                    );
                            };

                        fun do_fsig  is_light
                            =
                            pprint_vdecl ("f" + p,  topfunc_ty p (spec, arg_names))
                            where
                                p =   is_light ?? "'" :: "";
                            end;

                        package_name_for_c_function
                            =
                            "package "   +   package_name_for_c_function  c_name;

                        my (do_f_heavy, incomplete)
                            =
                            ( (do_heavy  ?? (make_do_f FALSE)
                                         :: (\\ () = ())),
                              FALSE
                            )
                            except
                                INCOMPLETE =  ( \\ () = (),
                                                TRUE
                                              );

                        str "local";
                        wrapbox 4;
                        nl (); str "include package   c::dim;";
                        nl (); str "include package   c_internals;";
                        pprint_vdef ("handle", eapp (evar library_handle, estring c_name));
                        end_box ();
                        nl (); str "herein";
                        nl (); str (package_name_for_c_function + " : api");
                        wrapbox 4;
                        pprint_vdecl ("rtti", rtti_ty (s::FPTR spec));
                        pprint_vdecl ("fptr", arrow (void, witness_type (s::FPTR spec)));

                        if  (do_heavy and not incomplete)  do_fsig FALSE;  fi;
                        if  (do_light or      incomplete)  do_fsig TRUE;   fi;

                        end_box ();

                        nl (); str "end {";
                        wrapbox 4;

                        pprint_vdef ("rtti", rtti_val (s::FPTR spec));

                        pprint_function_def (
                            "fptr",
                            [eunit],
                            eapp (evar "make_fptr",
                                  etuple [evar (fptr_makecall spec),
                                          eapp (evar "handle", eunit)]));
                        do_f_heavy ();

                        if (do_light or incomplete)

                            make_do_f TRUE ();
                        fi;

                        end_box ();   nl ();
                        str "};";     nl ();
                        str "end;";   nl ();
                        close_pp ();

                        exported_packages :=  package_name_for_c_function ! *exported_packages;

                    };                                          # fun pprint_global_fun_pkg



                # Synthesize the master .lib file to compile
                # all the Mythryl files we've generated:
                #
                fun generate_makelib_file ()
                    =
                    {   file =   descrfile  makelib_file;
                        #
                        (open_pp (file, NULL))
                            ->
                            { close_pp, line, str, nl, vbox, end_box, ... };

                        str "(primitive c-internals)";
                        nl ();
                        nl ();
                        nl ();
                        line "LIBRARY_EXPORTS";
                        nl ();
                        vbox 4;
                        apply line *exported_packages;
                        end_box ();
                        nl ();
                        nl ();
                        nl ();
                        str "LIBRARY_COMPONENTS";
                        nl ();
                        vbox 4;

                        apply
                            line
                            [ "$ROOT/src/lib/std/standard.lib",
                              "$ROOT/src/lib/c-glue-lib/internals/c-internals.lib",
                              "$ROOT/src/lib/core/init/init.cmi:  cm"
                            ];

                        apply
                            line
                            *makelib_files;

                        end_box ();
                        nl ();
                        close_pp ();
                    };



                # Generate all the result .pkg files:

                im::apply  pprint_fptr_rtti               fptr_types;

                sm::apply  pprint_struct_pkg              structs;
                sm::apply  pprint_union_pkg               unions;
                sm::apply  pprint_enum_pkg                enums;

                ss::apply  pprint_incomplete_struct_pkg   incomplete_structs;
                ss::apply  pprint_incomplete_union_pkg    incomplete_unions;
                ss::apply  pprint_incomplete_enum_pkg     incomplete_enums;

                sm::apply  pprint_struct_accessors_pkg    structs;
                sm::apply  pprint_union_accessors_pkg     unions;
                sm::apply  pprint_enum_accessors_pkg      enums;

                apply      pprint_global_type_pkg          global_types;
                apply      pprint_global_var_pkg          global_variables;
                apply      pprint_global_fun_pkg          global_functions;

                generate_makelib_file ();

            };  # fun gen
    };          # package gen
end;            # stipulate








Comments and suggestions to: bugs@mythryl.org

PreviousUpNext