PreviousUpNext

15.4.518  src/lib/compiler/back/top/translate/translate-deep-syntax-pattern-to-lambdacode.pkg

## translate-deep-syntax-pattern-to-lambdacode.pkg 
#
# Compile surface-syntax pattern-match expressions from
# deep syntax down to lambdacode form.
#
# See also:    src/lib/compiler/back/low/tools/match-compiler/match-compiler-g.pkg
#              src/lib/compiler/back/low/tools/match-compiler/match-gen-g.pkg
#              src/lib/compiler/back/low/tools/doc/nowhere.tex
#
# Mythryl uses pattern matching in a number of contexts:
#
#     pattern = expression;            # Example:  RECORD { foo=x, bar=y } = f(z);
#     except pattern => expression     # Example  ... except RECORD { foo=x, bar=y } => (x,y);
#     case x of pattern => expression  # Example:  case x of RECORD { foo=x, bar=y } => (x,y);
#     fun pattern = expression         # Example   fun  myfn RECORD { foo=x, bar=y } =  (x,y);
#
# (The last two cases are essentially identical,
# 'fun' being syntactic sugar for a naming of
# a '\\' containing a case statement.)
#
# At the raw syntax and deep syntax levels,
# we just represent such patterns as syntax
# trees reflecting surface syntax.
#
# Our lambdacode intermediate language, however,
# which is based closely on a typed polymorphic
# lambda calculus, has no such special syntax
# for pattern-matching, so when we translate
# from deep syntax into lambdacode, we must compile
# pattern-matching down into regular function applications.
#
# That is our job in this file.
#
# Deep syntax is defined in
#
#     src/lib/compiler/front/typer-stuff/deep-syntax/deep-syntax.api
#
# The "lambdacode" intermediate language is defined in
#
#     src/lib/compiler/back/top/lambdacode/lambdacode-form.api
#
# Translation between the two is done by
#
#     src/lib/compiler/back/top/translate/translate-deep-syntax-to-lambdacode.pkg
#
# which invokes us to handle compiling pattern syntax
# into lambdacode code.
#
# We have three entry points, corresponding to the three
# basic contexts in which pattern-matching is done:
#     namings            # First example above
#     'except' handling  # Second example above
#     'case' and 'fun'   # Third and fourth examples above.
#
# See also:
#
#     SML/NJ Match Compiler Notes
#     William Aitken
#     1992, 15p
#     http://www.smlnj.org//compiler-notes/matchcomp.ps

# Compiled by:
#     src/lib/compiler/core.sublib



###        "It is not because things are
###         difficult that we do not dare,
###         it is because we do not dare
###         that they are difficult."
###
###                      -- Seneca



###        "A heart in love with beauty never grows old."
###
###                                  -- Turkish proverb



###        "I don't want it good.   I want it Tuesday."
###
###                              -- Jack Warner



###        "You need the willingness to fail all the time.
###         You have to generate many ideas and then you have
###         to work very hard only to discover that they don't work.
###         And you keep doing that over and over until you
###         find one that does work."
###
###                                    -- John W Backus



#DO set_control "compiler::trap_int_overflow" "TRUE";

stipulate
    package ds  =  deep_syntax;                                 # deep_syntax                   is from   src/lib/compiler/front/typer-stuff/deep-syntax/deep-syntax.pkg
    package err =  error_message;                               # error_message                 is from   src/lib/compiler/front/basics/errormsg/error-message.pkg
    package hcf =  highcode_form;                               # highcode_form                 is from   src/lib/compiler/back/top/highcode/highcode-form.pkg
    package hut =  highcode_uniq_types;                         # highcode_uniq_types           is from   src/lib/compiler/back/top/highcode/highcode-uniq-types.pkg
    package lcf =  lambdacode_form;                             # lambdacode_form               is from   src/lib/compiler/back/top/lambdacode/lambdacode-form.pkg
    package syx =  symbolmapstack;                              # symbolmapstack                is from   src/lib/compiler/front/typer-stuff/symbolmapstack/symbolmapstack.pkg
    package tdt =  type_declaration_types;                      # type_declaration_types        is from   src/lib/compiler/front/typer-stuff/types/type-declaration-types.pkg
    package tmp =  highcode_codetemp;                           # highcode_codetemp             is from   src/lib/compiler/back/top/highcode/highcode-codetemp.pkg
herein

    api Translate_Deep_Syntax_Pattern_To_Lambdacode {


        To_Tc_Lt =   (tdt::Typoid -> hut::Uniqtype,   tdt::Typoid -> hut::Uniqtypoid);

        Make_Integer_Switch
            =
            (lcf::Lambdacode_Expression, List ((multiword_int::Int, lcf::Lambdacode_Expression)), lcf::Lambdacode_Expression)
            ->
            lcf::Lambdacode_Expression;

        compile_naming_pattern
            :
            ( syx::Symbolmapstack,
              List( (ds::Case_Pattern, lcf::Lambdacode_Expression) ),
              (lcf::Lambdacode_Expression -> lcf::Lambdacode_Expression),
              tmp::Codetemp,
              To_Tc_Lt,
              err::Plaint_Sink,
              Make_Integer_Switch
            )
            ->
            lcf::Lambdacode_Expression;

        compile_case_pattern
            :
            ( syx::Symbolmapstack,
              List( (ds::Case_Pattern, lcf::Lambdacode_Expression) ),
              (lcf::Lambdacode_Expression -> lcf::Lambdacode_Expression),
              tmp::Codetemp,
              To_Tc_Lt,
              err::Plaint_Sink,
              Make_Integer_Switch
            )
            ->
            lcf::Lambdacode_Expression;

        compile_exception_pattern
            :
            ( syx::Symbolmapstack,
              List( (ds::Case_Pattern, lcf::Lambdacode_Expression) ),
              (lcf::Lambdacode_Expression -> lcf::Lambdacode_Expression),
              tmp::Codetemp,
              To_Tc_Lt,
              err::Plaint_Sink,
              Make_Integer_Switch
            )
            ->
            lcf::Lambdacode_Expression;

    };
end;

stipulate
    package cos =  compile_statistics;                                  # compile_statistics                                    is from   src/lib/compiler/front/basics/stats/compile-statistics.pkg
    package ds  =  deep_syntax;                                         # deep_syntax                                           is from   src/lib/compiler/front/typer-stuff/deep-syntax/deep-syntax.pkg
    package err =  error_message;                                       # error_message                                         is from   src/lib/compiler/front/basics/errormsg/error-message.pkg
    package hbo =  highcode_baseops;                                    # highcode_baseops                                      is from   src/lib/compiler/back/top/highcode/highcode-baseops.pkg
    package hcf =  highcode_form;                                       # highcode_form                                         is from   src/lib/compiler/back/top/highcode/highcode-form.pkg
    package hut =  highcode_uniq_types;                                 # highcode_uniq_types                                   is from   src/lib/compiler/back/top/highcode/highcode-uniq-types.pkg
    package lcf =  lambdacode_form;                                     # lambdacode_form                                       is from   src/lib/compiler/back/top/lambdacode/lambdacode-form.pkg
    package ln  =  literal_to_num;                                      # literal_to_num                                        is from   src/lib/compiler/src/stuff/literal-to-num.pkg
    package mtt =  more_type_types;                                     # more_type_types                                       is from   src/lib/compiler/front/typer/types/more-type-types.pkg
    package plj =  translate_deep_syntax_pattern_to_lambdacode_junk;    # translate_deep_syntax_pattern_to_lambdacode_junk      is from   src/lib/compiler/back/top/translate/translate-deep-syntax-pattern-to-lambdacode-junk.pkg
    package pp  =  standard_prettyprinter;                              # standard_prettyprinter                                is from   src/lib/prettyprint/big/src/standard-prettyprinter.pkg
    package sl  =  sorted_list;                                         # sorted_list                                           is from   src/lib/compiler/back/low/library/sorted-list.pkg
    package tdt =  type_declaration_types;                              # type_declaration_types                                is from   src/lib/compiler/front/typer-stuff/types/type-declaration-types.pkg
    package tmp =  highcode_codetemp;                                   # highcode_codetemp                                     is from   src/lib/compiler/back/top/highcode/highcode-codetemp.pkg
    package tyj =  type_junk;                                           # type_junk                                             is from   src/lib/compiler/front/typer-stuff/types/type-junk.pkg
    package tx  =  template_expansion;                                  # template_expansion                                    is from   src/lib/compiler/back/top/translate/template-expansion.pkg
    package vac =  variables_and_constructors;                          # variables_and_constructors                            is from   src/lib/compiler/front/typer-stuff/deep-syntax/variables-and-constructors.pkg
    package vh  =  varhome;                                             # varhome                                               is from   src/lib/compiler/front/typer-stuff/basics/varhome.pkg
    #
    package mp  =  prettyprint_lambdacode_expression;                   # prettyprint_lambdacode_expression                     is from   src/lib/compiler/back/top/lambdacode/prettyprint-lambdacode-expression.pkg
    #
#   include package  translate_deep_syntax_pattern_to_lambdacode_junk;
herein 

    package   translate_deep_syntax_pattern_to_lambdacode
    : (weak)  Translate_Deep_Syntax_Pattern_To_Lambdacode
    {
        intersect      =   sl::intersect;
        union          =   sl::merge;
        set_difference =   sl::difference;
        #
        fun is_there (i, set)
            =
            sl::member set i;
        #
        fun bug s
            =
            err::impossible ("translate_deep_syntax_pattern_to_lambdacode: " + s);

        say =   global_controls::print::say;

        To_Tc_Lt
            =
            ( tdt::Typoid -> hut::Uniqtype,
              tdt::Typoid -> hut::Uniqtypoid
            );

        Make_Integer_Switch
            =
            ( lcf::Lambdacode_Expression,
              List ((multiword_int::Int, lcf::Lambdacode_Expression)),
              lcf::Lambdacode_Expression
            )
            ->
            lcf::Lambdacode_Expression;


        # MAJOR CLEANUP REQUIRED ! The function make_var is currently directly taken 
        # from the highcode_codetemp module; I think it should be taken from the 
        # "comp_info". Similarly, should we replace all issue_highcode_codetemp in the backend
        # with the make_var in "compInfo" ? (ZHONG)                    XXX BUGGO FIXME
        #
        make_var =   tmp::issue_highcode_codetemp;
        #
        fun abstest0 _ =   bug "abstest0 unimplemented";
        fun abstest1 _ =   bug "abstest1 unimplemented";

        # Translating the type field in VALCON
        # into Uniqtypoid; constant valcons 
        # will take void_uniqtypoid as the argument
        #
        fun to_valcon_lty  to_lambda_type  type
            =
            case type 
                #             
                tdt::TYPESCHEME_TYPOID
                   { typescheme_eqflags => an_api,
                     typescheme => tdt::TYPESCHEME { arity, body }
                   }
                   =>
                   if (mtt::is_arrow_type body)
                        to_lambda_type type;
                   else to_lambda_type (  tdt::TYPESCHEME_TYPOID
                                            { typescheme_eqflags => an_api, 
                                              typescheme                   => tdt::TYPESCHEME
                                                                                 { arity,
                                                                                   body  => mtt::(-->) (mtt::void_typoid, body)
                                                                                 }
                                            }
                                       );
                   fi;

                _   => if (mtt::is_arrow_type type)
                            to_lambda_type type;
                       else to_lambda_type (mtt::(-->) (mtt::void_typoid, type));
                       fi;
            esac;


        #########################################################################

        And_Or

          = AND  { namings:  List( (Int, vac::Variable) ),
                   subtrees:  List( And_Or ),
                   constraints:  List( (plj::Dconinfo, List( Int ),  Null_Or( And_Or )) )
                 }

          | CASE { namings:  List( (Int, vac::Variable) ),
                   an_api:  vh::Valcon_Signature,
                   cases:   List( (plj::Path_Constant, List( Int ), List( And_Or )) ),
                   constraints:  List( (plj::Dconinfo, List( Int ),  Null_Or( And_Or )) )
                 }

          | LEAF { namings:  List( (Int, vac::Variable) ),
                   constraints:  List( (plj::Dconinfo, List( Int ),  Null_Or( And_Or )) )
                 }
          ;


        Decision
          = CASE_DECISION    (plj::Path, vh::Valcon_Signature, List ((plj::Path_Constant, List( Int ), List( Decision )) ), List( Int ))
          | ABSCON_DECISION  (plj::Path, plj::Dconinfo, List( Int ), List( Decision ), List( Int ))
          | BIND_DECISION    (plj::Path, List( Int ))
          ;

        #
        fun all_conses (hds, tls)
            = 
            list::cat
                (   map (\\ hd =   (map   (\\ tl = hd ! tl)   tls))
                        hds
                );

        #
        fun or_expand (ds::OR_PATTERN (pattern1, pattern2))
                => 
                (or_expand pattern1)
                @
                (or_expand pattern2); 

            or_expand (pattern as ds::RECORD_PATTERN { fields, ... } )
                =>
                map (plj::make_recordpat  pattern)
                    (fold_backward all_conses [NIL] (map (or_expand o #2) fields));

            or_expand (ds::VECTOR_PATTERN (pats, t))
                =>
                map (\\ p = ds::VECTOR_PATTERN (p, t))
                    (fold_backward all_conses [NIL] (map or_expand pats));

            or_expand (ds::APPLY_PATTERN (k, t, pattern))
                =>
                map (\\ pattern = ds::APPLY_PATTERN (k, t, pattern))
                    (or_expand pattern);

            or_expand (ds::TYPE_CONSTRAINT_PATTERN (pattern, _))
                =>
                or_expand pattern;

            or_expand (ds::AS_PATTERN (ds::TYPE_CONSTRAINT_PATTERN (lpat, _), bpat))
                =>
                or_expand (ds::AS_PATTERN (lpat, bpat));

            or_expand (ds::AS_PATTERN (lpat, bpat))
                =>
                map (\\ pattern = ds::AS_PATTERN (lpat, pattern))
                    (or_expand bpat);

            or_expand pattern
                => 
                [pattern];
        end;

        #
        fun get_variable (v as vac::PLAIN_VARIABLE { path=>p1, ... },
                              (vac::PLAIN_VARIABLE { path=>p2, ... }, value) ! rest)
                =>
                symbol_path::equal (p1, p2)
                    ??  value
                    ::  get_variable (v, rest);

            get_variable (vac::PLAIN_VARIABLE _, [])
                =>
                bug "unbound 18";

            get_variable _ =>   bug "[mc::get_variable]";
        end;

        #
        fun path_instantiate_simple_expression variable_dictionary (plj::VARSIMP v)
                =>
                get_variable (v, variable_dictionary);

            path_instantiate_simple_expression variable_dictionary (plj::RECORDSIMP labsimps)
                => 
                plj::RECORD_PATH (map (path_instantiate_simple_expression variable_dictionary o #2) labsimps);
        end;

        #
        fun expand_namings (variable_dictionary, path_dictionary, NIL)
                =>
                NIL;

            expand_namings (variable_dictionary, path_dictionary, v ! rest)
                =>
                (path_instantiate_simple_expression path_dictionary (tx::fully_expand_naming variable_dictionary (plj::VARSIMP v)))
                 !
                (expand_namings (variable_dictionary, path_dictionary, rest));
        end;

        #
        fun named_variables (ds::VARIABLE_IN_PATTERN v)                => [v];
            named_variables (ds::TYPE_CONSTRAINT_PATTERN (pattern, _)) => named_variables pattern;
            named_variables (ds::AS_PATTERN (pattern1, pattern2))      => (named_variables (pattern1))@(named_variables (pattern2));

            named_variables (ds::APPLY_PATTERN (k, t, pattern))        => named_variables pattern;
            named_variables (ds::RECORD_PATTERN { fields, ... } )      => list::cat (map (named_variables o #2) fields);

            named_variables (ds::VECTOR_PATTERN (pats, _))             => list::cat (map named_variables pats);
            named_variables (ds::OR_PATTERN (pattern1, _))             => named_variables pattern1;

            named_variables _ => NIL;
        end;
        #
        fun pattern_namings (ds::VARIABLE_IN_PATTERN v, path)
                =>
                [(v, path)];

            pattern_namings (ds::TYPE_CONSTRAINT_PATTERN (pattern, _), path)
                =>
                pattern_namings (pattern, path);

            pattern_namings (ds::AS_PATTERN (pattern1, pattern2), path)
                => 
                (pattern_namings (pattern1, path))
                @
                (pattern_namings (pattern2, path));

            pattern_namings (ds::APPLY_PATTERN (k, t, pattern), path)
                => 
                pattern_namings (pattern, plj::DELTA_PATH (plj::DATAPCON (k, t), path));

            pattern_namings (ds::RECORD_PATTERN { fields, ... }, path)
                => 
                make (0, fields)
                where
                    fun make (n, NIL)
                            =>
                            NIL;

                       make (n, (lab, pattern) ! rest)
                            => 
                            (pattern_namings (pattern, plj::PI_PATH (n, path))) @ (make (n+1, rest));
                    end;
                end;

            pattern_namings (ds::VECTOR_PATTERN (pats, t), path)
                => 
                make (0, pats)
                where
                    fun make (n, NIL)
                            =>
                            NIL;

                       make (n, pattern ! rest)
                            => 
                            (pattern_namings (pattern, plj::VPI_PATH (n, t, path)))
                            @
                            (make (n+1, rest));
                    end;
                end;

            pattern_namings (ds::OR_PATTERN _, _)
                =>
                bug "Unexpected or pattern";

            pattern_namings _
                =>
                NIL;
        end;
        #
        fun pattern_paths (pattern, constrs)
            =
            constr_paths (constrs, pattern_dictionary, NIL)
            where
                pattern_dictionary =   pattern_namings (pattern, plj::ROOT_PATH);
                #
                fun constr_paths (NIL, dictionary, acc)
                        => 
                        ( (plj::ROOT_PATH, pattern) ! (reverse acc),
                          dictionary
                        );

                    constr_paths ((simpexp, cpat) ! rest, dictionary, acc)
                        => 
                        {   guard_path     =   path_instantiate_simple_expression  dictionary  simpexp;
                            #
                            new_dictionary =   pattern_namings (cpat, guard_path);

                            constr_paths (rest, dictionary@new_dictionary, (guard_path, cpat) ! acc);
                        };
                end;
            end;

        #
        fun var_to_lambda_var (vac::PLAIN_VARIABLE { varhome=>vh::HIGHCODE_VARIABLE v, vartypoid_ref, ... }, to_lambda_type)
                =>
                ( v,
                  to_lambda_type  *vartypoid_ref
                );

            var_to_lambda_var _
                =>
                bug "bug variable in mc::sml";
        end;

        #
        fun preprocess_pattern   to_lambda_type   (pattern, rhs)                # "rhs" == "right hand side"
            =
            {   namings =   named_variables pattern;
                #
                fname   =   make_var ();

                fun make_rhs_fun ([], rhs)
                        =>
                        lcf::FN (make_var(), hcf::void_uniqtypoid, rhs);

                    make_rhs_fun ([v], rhs)
                        => 
                        {   (var_to_lambda_var (v, to_lambda_type))
                                ->
                                (arg_var, argt);

                            lcf::FN (arg_var, argt, rhs);
                        };

                    make_rhs_fun (vl, rhs)
                        =>
                        {   arg_var =  make_var ();
                            #
                            fun foo (NIL, n)
                                    =>
                                    (rhs, NIL);

                               foo (v ! vl, n)
                                    => 
                                    {   my  (lv, lt)
                                            =
                                            var_to_lambda_var (v, to_lambda_type);

                                        my  (le, tt)
                                            =
                                            foo (vl, n+1);

                                        (lcf::LET (lv, lcf::GET_FIELD (n, lcf::VAR arg_var), le), lt ! tt);
                                    };
                            end;

                            (foo (vl, 0)) ->   (body, tt);

                            lcf::FN (arg_var, hcf::make_tuple_uniqtypoid tt, body);
                        };
                end;

                rhs_fun =   make_rhs_fun (namings, rhs);

                pats    =   or_expand pattern;

                #
                fun expand (pattern ! rest)
                        =>
                        {   (tx::template_expand_pattern  pattern)
                                ->
                                (new_pattern,  constrs,  variable_dictionary);

                            (pattern_paths  (new_pattern, constrs))
                                ->
                                (new_list, path_dictionary);

                            naming_paths
                                =
                                expand_namings (variable_dictionary, path_dictionary, namings);

                            (new_list, naming_paths, fname)  !  (expand rest);
                        }
                        except
                            tx::CANNOT_MATCH =   ( [ (plj::ROOT_PATH, ds::NO_PATTERN) ], NIL, fname) ! (expand rest);

                    expand NIL =>  NIL;
                end;

                ( expand pats,
                  (fname, rhs_fun)
                );
            };
        #
        fun make_and_or (match_rep, err)
            =
            {   fun add_naming (v, rule, AND { namings, subtrees, constraints } )
                        =>
                        AND { namings=>(rule, v) ! namings, subtrees, 
                            constraints };

                    add_naming (v, rule, CASE { namings, an_api, cases, constraints } )
                        =>
                        CASE { namings=>(rule, v) ! namings, cases, an_api, constraints };

                    add_naming (v, rule, LEAF { namings, constraints } )
                        =>
                        LEAF { namings=>(rule, v) ! namings, constraints };
                end;

                #
                fun word_con (s, t, msg)
                    = 
                    {   fun conv (wrap_g, conv_g)
                            =
                            wrap_g (
                                conv_g s
                                except
                                    OVERFLOW
                                        =
                                        {   err err::ERROR
                                               ( "out-of-range word literal in pattern: 0w"
                                                 +
                                                 multiword_int::to_string s
                                               )
                                               err::null_error_body;

                                            conv_g (multiword_int::from_int 0);
                                        }
                            );


                        if (tyj::typoids_are_equal (t, mtt::unt_typoid))
                            #                       
                            conv (plj::UNTPCON, ln::unt);                       #  plj::UNTPCON (ln::word s) 
                        else
                            if (tyj::typoids_are_equal (t, mtt::unt8_typoid))
                                #
                                conv (plj::UNTPCON, ln::one_byte_unt);          #  plj::UNTPCON (ln::word8 s) 

                            elif (tyj::typoids_are_equal (t, mtt::unt1_typoid))

                                conv (plj::UNT1PCON, ln::one_word_unt);         #  plj::UNT1PCON (ln::one_word_unt s) 
                            else
                                bug msg;
                            fi;
                        fi;
                    };
                #
                fun num_con (s, t, msg)
                    = 
                    if (tyj::typoids_are_equal (t, mtt::int_typoid))
                        #  
                        plj::INTPCON (ln::int s); 

                    elif (tyj::typoids_are_equal (t, mtt::int1_typoid))
                             
                        plj::INT1PCON (ln::one_word_int s);

                    elif (tyj::typoids_are_equal (t, mtt::multiword_int_typoid) )

                        plj::INTEGERPCON s;
                    else
                        word_con (s, t, msg);
                    fi;

                #
                fun add_a_constraint (k, NULL, rule, NIL)
                        =>
                        [ (k, [rule], NULL) ];

                    add_a_constraint (k, THE pattern, rule, NIL)
                        =>
                        [(k, [rule], THE (make_and_or (pattern, rule)))];

                    add_a_constraint (k, patopt as THE pattern, rule, 
                                   (constr as (k', rules, THE subtree)) ! rest)
                        =>
                        if (plj::con_eq' (k, k'))
                            #                       
                            (k, rule ! rules, THE (merge_and_or (pattern, subtree, rule))) ! rest;
                        else 
                            constr ! (add_a_constraint (k, patopt, rule, rest));
                        fi;

                    add_a_constraint (k, NULL, rule, (constr as (k', rules, NULL)) ! rest)
                        =>
                        if (plj::con_eq' (k, k'))
                            #
                            (k, rule ! rules, NULL) ! rest;
                        else
                            constr ! (add_a_constraint (k, NULL, rule, rest));
                        fi;

                    add_a_constraint (k, patopt, rule, (constr as (k', rules, _)) ! rest)
                        =>
                        if (plj::con_eq' (k, k'))   bug "arity conflict";
                        else                   constr ! (add_a_constraint (k, patopt, rule, rest));
                        fi;
                end 

                also
                fun add_constraint (k, patopt, rule, AND { namings, subtrees, constraints } )
                        =>
                        AND { namings, subtrees, 
                              constraints=>add_a_constraint (k, patopt, rule, constraints) };

                    add_constraint (k, patopt, rule, CASE { namings, an_api, cases, 
                                                        constraints } )
                        =>
                        CASE { namings, cases, an_api,
                             constraints=>add_a_constraint (k, patopt, rule, constraints) };

                    add_constraint (k, patopt, rule, LEAF { namings, constraints } )
                        =>
                        LEAF { namings, 
                             constraints=>add_a_constraint (k, patopt, rule, constraints) };
                end 

                also
                fun make_and_or (ds::VARIABLE_IN_PATTERN v, rule)
                        =>
                        LEAF { namings => [(rule, v)], constraints => NIL };

                    make_and_or (ds::WILDCARD_PATTERN, rule)
                        =>
                        LEAF { namings => NIL, constraints => NIL };

                    make_and_or (ds::TYPE_CONSTRAINT_PATTERN (pattern, _), rule)
                        =>
                        make_and_or (pattern, rule);

                    make_and_or (ds::AS_PATTERN (ds::TYPE_CONSTRAINT_PATTERN (lpat, _), bpat), rule)
                        =>
                        make_and_or (ds::AS_PATTERN (lpat, bpat), rule);

                    make_and_or (ds::AS_PATTERN (ds::VARIABLE_IN_PATTERN v, bpat), rule)
                          =>
                          add_naming (v, rule, make_and_or (bpat, rule));

                    make_and_or (ds::AS_PATTERN (ds::CONSTRUCTOR_PATTERN (k, t), bpat), rule)
                          =>
                          add_constraint ((k, t), NULL, rule, make_and_or (bpat, rule));

                    make_and_or (ds::AS_PATTERN (ds::APPLY_PATTERN (k, t, lpat), bpat), rule)
                          =>
                          add_constraint ((k, t), THE lpat, rule, make_and_or (bpat, rule));

                    make_and_or (ds::INT_CONSTANT_IN_PATTERN (s, t), rule)
                        => 
                        if (tyj::typoids_are_equal (t, mtt::int2_typoid))
                            # 
                            make_and_or_64 (ln::two_word_int s, rule);
                        else
                            con =   num_con (s, t, "make_and_or ds::INT_CONSTANT_IN_PATTERN");

                            CASE {
                                namings    =>   NIL,
                                constraints =>   NIL,
                                an_api =>   vh::NULLARY_CONSTRUCTOR,
                                cases       =>   [ (con, [rule], NIL) ]
                            };
                        fi;

                    make_and_or (ds::UNT_CONSTANT_IN_PATTERN (s, t), rule)
                        => 
                        if (tyj::typoids_are_equal (t, mtt::unt2_typoid))
                            #                       
                            make_and_or_64 (ln::two_word_unt s, rule);
                        else
                            con =   word_con (s, t, "make_and_or ds::UNT_CONSTANT_IN_PATTERN");

                            CASE {
                                namings     =>  NIL,
                                constraints =>  NIL,
                                an_api      =>  vh::NULLARY_CONSTRUCTOR,
                                cases       =>  [(con, [rule], NIL)]
                            };
                        fi;

                    make_and_or (ds::FLOAT_CONSTANT_IN_PATTERN r, rule)
                        =>
                        CASE { namings     =>  NIL,
                               constraints =>  NIL,
                               an_api      =>  vh::NULLARY_CONSTRUCTOR,
                               cases       =>  [(plj::REALPCON r, [rule], NIL)]
                             };

                    make_and_or (ds::STRING_CONSTANT_IN_PATTERN s, rule)
                        =>
                        CASE { namings     =>  NIL,
                               constraints =>  NIL,
                               an_api      =>  vh::NULLARY_CONSTRUCTOR,
                               cases       =>  [(plj::STRINGPCON s, [rule], NIL)]
                             };


                    # NOTE: the following won't work for cross compiling 
                    #      to multi-byte characters. XXX BUGGO FIXME


                    make_and_or (ds::CHAR_CONSTANT_IN_PATTERN s, rule)
                        =>
                        CASE { namings     =>  NIL,
                               constraints =>  NIL,
                               an_api      =>  vh::NULLARY_CONSTRUCTOR,
                               cases       =>  [(plj::INTPCON (string::get_byte (s, 0)), [rule], NIL)]
                             };

                    make_and_or (ds::RECORD_PATTERN { fields, ... }, rule)
                        =>
                        AND { namings     =>  NIL,
                              constraints =>  NIL, 
                              subtrees    =>  multi_fn (map #2 fields, rule)
                            };

                    make_and_or (ds::VECTOR_PATTERN (pats, t), rule)
                        =>
                        CASE { namings     =>  NIL,
                               constraints =>  NIL,
                               an_api      =>  vh::NULLARY_CONSTRUCTOR,
                               cases       =>  [ (plj::VLENPCON (length pats, t), [rule], 
                                                 multi_fn (pats, rule)) ]
                             };

                    make_and_or (ds::CONSTRUCTOR_PATTERN (k, t), rule)
                      =>
                      if (plj::abstract k)
                          # 
                          LEAF { namings => NIL, constraints => [((k, t), [rule], NULL)] };
                      else
                          CASE { namings => NIL, constraints => NIL,
                                 an_api  => plj::signature_of_constructor k,
                                 cases   => [(plj::DATAPCON (k, t), [rule], NIL)]
                               };
                      fi;

                    make_and_or (ds::APPLY_PATTERN (k, t, pattern), rule)
                        =>
                        if (plj::abstract k)
                            #
                            LEAF { namings => NIL, 
                                   constraints => [((k, t), [rule], THE (make_and_or (pattern, rule)))]
                                 };
                        else
                            CASE { namings => NIL, constraints => NIL, an_api => plj::signature_of_constructor k,
                                   cases => [(plj::DATAPCON (k, t), [rule], [make_and_or (pattern, rule)])]
                                 };
                        fi;

                    make_and_or _
                        =>
                        bug "genandor applied to inapplicable pattern";
                end 


                # Simulate 64-bit words and ints as pairs of 32-bit words 

                also
                fun make_and_or_64 ((hi, lo), rule)
                    =
                    {   fun p32 w
                            =
                            ds::UNT_CONSTANT_IN_PATTERN (one_word_unt::to_multiword_int w, mtt::unt1_typoid);

                         make_and_or (deep_syntax_junk::tuplepat [p32 hi, p32 lo], rule);
                    }

                also
                fun multi_fn (NIL, rule)
                        =>
                        NIL;

                    multi_fn (pattern ! rest, rule)
                        =>
                        (make_and_or (pattern, rule)) ! multi_fn((rest, rule));
                end 

                also
                fun merge_and_or (ds::VARIABLE_IN_PATTERN v, and_or, rule)
                        =>
                        add_naming (v, rule, and_or);

                    merge_and_or (ds::WILDCARD_PATTERN, and_or, rule)
                        =>
                        and_or;

                    merge_and_or (ds::TYPE_CONSTRAINT_PATTERN (pattern, _), and_or, rule)
                        => 
                        merge_and_or (pattern, and_or, rule);

                    merge_and_or (ds::AS_PATTERN (ds::TYPE_CONSTRAINT_PATTERN (lpat, _), bpat), and_or, rule)
                        =>
                        merge_and_or (ds::AS_PATTERN (lpat, bpat), and_or, rule);

                    merge_and_or (ds::AS_PATTERN (ds::VARIABLE_IN_PATTERN v, bpat), and_or, rule)
                        =>
                        add_naming (v, rule, merge_and_or (bpat, and_or, rule));

                    merge_and_or (ds::AS_PATTERN (ds::CONSTRUCTOR_PATTERN (k, t), bpat), and_or, rule)
                        =>
                        add_constraint ((k, t), NULL, rule, merge_and_or (bpat, and_or, rule));

                    merge_and_or (ds::AS_PATTERN (ds::APPLY_PATTERN (k, t, lpat), bpat), and_or, rule)
                        =>
                        add_constraint ((k, t), THE lpat, rule, merge_and_or (bpat, and_or, rule));

                    merge_and_or (ds::CONSTRUCTOR_PATTERN (k, t), LEAF { namings, constraints }, rule)
                        =>
                        if (plj::abstract k)
                            #
                            LEAF { namings => NIL, 
                                   constraints => add_a_constraint((k, t), NULL, rule, constraints)
                                 };
                        else
                            CASE { namings => NIL, constraints => NIL, an_api => plj::signature_of_constructor k,
                                   cases => [(plj::DATAPCON (k, t), [rule], NIL)]
                                 };
                        fi;

                    merge_and_or (ds::APPLY_PATTERN (k, t, pattern), LEAF { namings, constraints }, rule)
                        =>
                        if (plj::abstract k)
                            #
                            LEAF { namings, constraints => add_a_constraint((k, t), THE pattern, rule, constraints) };
                        else
                            CASE { namings, constraints, 
                                   an_api => plj::signature_of_constructor k,
                                   cases => [(plj::DATAPCON (k, t), [rule], [make_and_or (pattern, rule)])]
                                 };
                        fi;

                    merge_and_or (pattern, LEAF { namings, constraints }, rule)
                        =>
                        case (make_and_or (pattern, rule))
                            #
                            CASE { namings=>NIL, constraints=>NIL, an_api, cases }
                                =>
                                CASE { namings, an_api, constraints, cases };

                            AND { namings=>NIL, constraints=>NIL, subtrees }
                                =>
                                AND { namings, constraints, subtrees };

                             _   => bug "make_and_or returned bogusly";
                        esac;

                    merge_and_or (ds::INT_CONSTANT_IN_PATTERN (s, t), c as CASE { namings, cases, constraints, an_api }, rule)
                        =>
                        if (tyj::typoids_are_equal (t, mtt::int2_typoid))
                            #                       
                            merge_and_or_64 (ln::two_word_int s, c, rule);
                        else
                            pcon =   num_con (s, t, "merge_and_or ds::INT_CONSTANT_IN_PATTERN");

                            CASE { namings, constraints, an_api, cases => add_a_case (pcon, NIL, rule, cases) };
                        fi;

                    merge_and_or (ds::UNT_CONSTANT_IN_PATTERN (s, t), c as CASE { namings, cases, 
                                                        constraints, an_api }, rule)
                        =>
                        if (tyj::typoids_are_equal (t, mtt::unt2_typoid))
                            #
                            merge_and_or_64 (ln::two_word_unt s, c, rule);
                        else
                            pcon =   word_con (s, t, "merge_and_or ds::UNT_CONSTANT_IN_PATTERN");

                            CASE { namings, constraints, an_api, cases => add_a_case (pcon, NIL, rule, cases) };
                        fi;

                    merge_and_or (ds::FLOAT_CONSTANT_IN_PATTERN r, CASE { namings, cases, constraints, an_api }, rule)
                        =>
                        CASE { namings, constraints, an_api, cases => add_a_case (plj::REALPCON r, NIL, rule, cases) };

                    merge_and_or (ds::STRING_CONSTANT_IN_PATTERN s, CASE { namings, cases, constraints, an_api }, rule)
                        =>
                        CASE { namings, constraints, an_api, cases => add_a_case (plj::STRINGPCON s, NIL, rule, cases) };


                    # NOTE: the following won't work for cross compiling 
                    # to multi-byte characters          XXX BUGGO FIXME

                    merge_and_or (ds::CHAR_CONSTANT_IN_PATTERN s, CASE { namings, cases, constraints, an_api }, rule)
                        =>
                        CASE { namings, constraints, an_api,
                               cases => add_a_case (plj::INTPCON (string::get_byte (s, 0)), 
                               NIL, rule, cases)
                             };

                    merge_and_or (ds::RECORD_PATTERN { fields, ... }, AND { namings, constraints, subtrees }, rule)
                        =>
                        AND { namings, constraints,   subtrees => multi_merge (map #2 fields, subtrees, rule) };

                    merge_and_or (ds::VECTOR_PATTERN (pats, t), CASE { namings, cases, an_api, constraints }, rule)
                        =>
                        CASE { namings, constraints, an_api, cases => add_a_case (plj::VLENPCON (length pats, t), pats, rule, cases) };

                    merge_and_or (ds::CONSTRUCTOR_PATTERN (k, t), CASE { namings, cases, constraints, an_api }, rule)
                        =>
                        if (plj::abstract k)
                            CASE { namings, cases, an_api,   constraints => add_a_constraint((k, t), NULL, rule, constraints) };
                        else
                            CASE { namings, constraints, an_api,   cases => add_a_case (plj::DATAPCON (k, t), NIL, rule, cases) };
                        fi;

                    merge_and_or (ds::APPLY_PATTERN (k, t, pattern), CASE { namings, cases, constraints, an_api }, rule)
                        =>
                        if (plj::abstract k)
                            CASE { namings, cases,  an_api,   constraints => add_a_constraint((k, t), THE pattern, rule, constraints) };
                        else
                            CASE { namings, constraints, an_api,   cases => add_a_case (plj::DATAPCON (k, t), [pattern], rule, cases) };
                        fi;

                    merge_and_or (ds::CONSTRUCTOR_PATTERN (k, t), AND { namings, constraints, subtrees }, rule)
                        =>
                        if (plj::abstract k)
                            AND { namings, subtrees,   constraints => add_a_constraint((k, t), NULL, rule, constraints) };
                        else
                            bug "concrete constructor can't match record";
                        fi;

                    merge_and_or (ds::APPLY_PATTERN (k, t, pattern), AND { namings, subtrees, constraints }, rule)
                        =>
                        if (plj::abstract k)
                            AND { namings, subtrees,   constraints => add_a_constraint((k, t), THE pattern, rule, constraints) };
                        else
                            bug "concrete constructor application can't match record";
                        fi;

                    merge_and_or _
                        =>
                        bug "bad pattern merge";
                end 

                # Simulate 64-bit words and ints as pairs of 32-bit words 

                also
                fun merge_and_or_64 ((hi, lo), c, rule)
                    =
                    {   fun p32 w
                            =
                            ds::UNT_CONSTANT_IN_PATTERN  (one_word_unt::to_multiword_int  w,  mtt::unt1_typoid);

                         merge_and_or (deep_syntax_junk::tuplepat [p32 hi, p32 lo], c, rule);
                    }

                also
                fun add_a_case (pcon, pats, rule, NIL)
                        =>
                        [ (pcon, [ rule ], multi_fn (pats, rule)) ];

                    add_a_case (pcon, pats, rule, 
                             (a_case as (pcon', rules, subtrees)) ! rest)
                        =>
                        if (plj::constant_eq (pcon, pcon'))
                            #
                            (pcon, rule ! rules, multi_merge (pats, subtrees, rule)) ! rest;
                        else 
                            a_case ! (add_a_case (pcon, pats, rule, rest));
                        fi;
                end 

                also
                fun multi_merge (NIL, NIL, rule)
                        =>
                        NIL;

                    multi_merge (pattern ! pats, subtree ! subtrees, rule)
                        =>
                        (merge_and_or (pattern, subtree, rule)) ! (multi_merge (pats, subtrees, rule));

                    multi_merge _
                        =>
                        bug "list length mismatch in multi_merge";
                end;

                #
                fun merge_pattern_with_and_or_list (path, pattern, NIL, n)
                        =>
                        [ (path, make_and_or (pattern, n)) ];

                    merge_pattern_with_and_or_list (path, pattern, (path', and_or) ! rest, n)
                        =>
                        if (plj::path_eq (path, path'))
                            #
                            (path, merge_and_or (pattern, and_or, n)) ! rest;
                        else
                            (path', and_or) ! (merge_pattern_with_and_or_list (path, pattern, rest, n));
                        fi;
                end;

                #
                fun make_and_or_list (NIL, n)
                        =>
                        bug "no patterns (gen)";

                    make_and_or_list ( [ (path, pattern) ], n)
                        =>
                        [ (path, make_and_or (pattern, n)) ];

                    make_and_or_list ((path, pattern) ! rest, n)
                        => 
                        merge_pattern_with_and_or_list
                            (path, pattern, make_and_or_list (rest, n), n);
                end;
                #
                fun merge_and_or_list (NIL, aol, n)
                        =>
                        bug "no patterns (merge)";

                    merge_and_or_list ([(path, pattern)], aol, n)
                        => 
                        merge_pattern_with_and_or_list (path, pattern, aol, n);

                    merge_and_or_list ((path, pattern) ! rest, aol, n)
                        => 
                        merge_pattern_with_and_or_list (path, pattern, merge_and_or_list (rest, aol, n), n);
                end;
                #
                fun make_and_or' (NIL, n)
                        =>
                        bug "no rules (make_and_or')";

                    make_and_or' ([(pats, _, _)], n)
                        => 
                        make_and_or_list (pats, n);

                    make_and_or' (([(_, ds::NO_PATTERN)], dictionary, namings) ! rest, n)
                        =>
                        make_and_or'(rest, n+1);

                    make_and_or' ((pats, dictionary, namings) ! rest, n)
                        =>
                        merge_and_or_list (pats, make_and_or'(rest, n+1), n);
                end;

                make_and_or' (match_rep, 0);            #  except Foo => raise exception (Internal 99)

            };   #  fun make_and_or 

        #
        fun add_a_naming (path, rule, NIL)
                =>
                [ BIND_DECISION (path, [ rule ] ) ];

            add_a_naming (path, rule, (bind as BIND_DECISION (path', rules)) ! rest)
                =>
                if (plj::path_eq (path, path'))
                    #                    
                    BIND_DECISION (path, rule ! rules)  !  rest;
                else
                    bind  !  (add_a_naming (path, rule, rest));
                fi; 

            add_a_naming _
                =>
                bug "non BIND_DECISION in naming list";
        end;


        #
        fun flatten_namings (NIL, path, active)
                =>
                NIL;

           flatten_namings (((rule, v) ! rest), path, active)
                =>
                if (is_there (rule, active))
                    #
                    add_a_naming (path, rule, flatten_namings (rest, path, active));
                else 
                    flatten_namings (rest, path, active);
                fi;
        end;


        #
        fun flatten_constraints (NIL, path, active)
                =>
                NIL;

            flatten_constraints ((di, rules, NULL) ! rest, path, active)
                => 
                {   yes_active =   intersect     (active, rules);
                    no_active  =   set_difference (active, rules);

                    rest' =   flatten_constraints (rest, path, active);

                    (ABSCON_DECISION (path, di, yes_active, NIL, no_active))
                    !
                    rest';
                };

            flatten_constraints ((di, rules, THE and_or) ! rest, path, active)
                => 
                {   yes_active =   intersect     (active, rules);
                    no_active  =   set_difference (active, rules);

                    rest' =   flatten_constraints (rest, path, active);

                    and_or'
                        = 
                        flatten_and_or (and_or, plj::DELTA_PATH (plj::DATAPCON di, path), active);

                    (ABSCON_DECISION (path, di, yes_active, and_or', no_active))
                    !
                    rest';
                };
        end 



        also
        fun flatten_and_or (AND { namings, subtrees, constraints }, path, active)
                =>
                {   btests = flatten_namings (namings, path, active);
                    #
                    fun do_tree (n, NIL)
                            =>
                            flatten_constraints (constraints, path, active);

                       do_tree (n, subtree ! rest)
                            =>
                            {   othertests = do_tree (n + 1, rest);

                                (flatten_and_or (subtree, plj::PI_PATH (n, path), active))
                                @
                                othertests;
                            };
                    end;

                    btests
                    @
                    (do_tree (0, subtrees));
                };

            flatten_and_or (CASE { namings, cases, constraints, an_api }, path, active)
                =>
                {   btests =   flatten_namings    (namings,    path, active);
                    ctests =   flatten_constraints (constraints, path, active);

                    btests
                    @
                    ((flatten_cases (cases, path, active, an_api)) ! ctests);
                };

            flatten_and_or (LEAF { namings, constraints }, path, active)
                =>
                {   btests = flatten_namings (namings, path, active);

                    btests
                    @
                    (flatten_constraints (constraints, path, active));
                };
        end 

        also
        fun flatten_a_case ((plj::VLENPCON (n, t), rules, subtrees), path, active, defaults)
                =>
                {   still_active =   intersect (union (rules, defaults), active);
                    rule_active  =   intersect (rules, active);
                    #
                    fun flatten_vsubs (n, NIL)
                            =>
                            NIL;

                       flatten_vsubs (n, subtree ! rest)
                            => 
                            (flatten_and_or (subtree, plj::VPI_PATH (n, t, path), still_active)) 
                            @
                            (flatten_vsubs (n + 1, rest));
                    end;

                    (plj::INTPCON n, rule_active, flatten_vsubs (0, subtrees));
                };

            flatten_a_case ((k as plj::DATAPCON (_, t), rules,[subtree]), path, active, defaults)
                =>
                {   still_active =   intersect (union (rules, defaults), active);
                    rule_active  =   intersect (rules, active);

                    new_patternh =   plj::DELTA_PATH (k, path);

                    (k, rule_active, flatten_and_or (subtree, new_patternh, still_active));
                };

            flatten_a_case ((constant, rules, NIL), path, active, defaults)
                =>
                (constant, intersect (rules, active), NIL);

            flatten_a_case _
                =>
                bug "illegal subpattern in a case";
        end 



        also
        fun flatten_cases (cases, path, active, an_api)
            =
            {   fun calculate_defaults (NIL, active)
                        =>
                        active;

                   calculate_defaults ((_, rules, _) ! rest, active)
                        =>
                        calculate_defaults (rest, set_difference (active, rules));
                end;

                defaults =   calculate_defaults (cases, active);
                #
                fun do_it NIL
                        =>
                        NIL;

                   do_it (a_case ! rest)
                        => 
                      ((flatten_a_case (a_case, path, active, defaults)) 
                       ! (do_it (rest)));
                end;

                case cases
                    #
                    (plj::VLENPCON (_, t), _, _) ! _
                        => 
                        CASE_DECISION (plj::VLEN_PATH (path, t), an_api, do_it cases, defaults);

                    cases =>   CASE_DECISION (path, an_api, do_it cases, defaults);
                esac;
            };
        #
        fun namings (n, l)
            =
            case (list::nth (l, n))
                #              
                (_, _, x) =>   x;
            esac;
        #
        fun path_constraints (plj::RECORD_PATH paths)
                => 
                list::cat (map path_constraints paths);

            path_constraints path
                =>
                [ path ];
        end;

        #
        fun flatten_and_ors (NIL, allrules)
                =>
                NIL;

            flatten_and_ors((path, and_or) ! rest, allrules)
                =>
                (path_constraints path, flatten_and_or (and_or, path, allrules))
                 !
                (flatten_and_ors (rest, allrules));
        end;

        #
        fun remove_path (path, path1 ! rest)
                =>
                plj::path_eq (path, path1)
                    ??  rest
                    ::  path1 ! (remove_path (path, rest));

            remove_path (path, NIL)
                =>
                NIL;
        end;

        #
        fun fire_constraint (path, (need_paths, decisions) ! rest, ready, delayed)
                =>
                case (remove_path (path, need_paths) )
                    #
                    NIL =>  fire_constraint (path, rest, decisions@ready, delayed);
                    x   =>  fire_constraint (path, rest, ready, (x, decisions) ! delayed);
                esac;

            fire_constraint (path, NIL, ready, delayed)
                =>
                (ready, delayed);
        end;

        #
        fun make_all_rules (NIL, _)
                =>
                NIL; 

            make_all_rules(([(plj::ROOT_PATH, ds::NO_PATTERN)], _, _) ! b, n)
                =>
                (make_all_rules (b, n + 1));

            make_all_rules(_ ! b, n)
                =>
                n ! (make_all_rules (b, n + 1));
        end;


        exception PICK_BEST;

        #
        fun relevent (CASE_DECISION(_, _, _, defaults), rulenum)
                => 
                not (is_there (rulenum, defaults));

            relevent (ABSCON_DECISION (_, _, _, _, defaults), rulenum)
                =>
                not (is_there (rulenum, defaults));

            relevent (BIND_DECISION _, _)
                => 
                bug "BIND_DECISION not fired";
        end;

        #
        fun metric (CASE_DECISION(_, _, cases,   defaults)) =>   (length defaults, length cases);
            metric (ABSCON_DECISION (_, _, _, _, defaults)) =>   (length defaults, 2);

            metric (BIND_DECISION _)
                =>
                bug "BIND_DECISION not fired (metric)";
        end;

        #
        fun metric_better ((a: Int, b: Int), (c, d))
            =
            a < c   or   (a == c and b < d);

        #
        fun do_pick_best (NIL, _, _, _, NULL ) =>   raise exception PICK_BEST;
            do_pick_best (NIL, _, _, _, THE n) =>   n;

            do_pick_best((BIND_DECISION _) ! rest, _, n,                         _, _) =>   n;
            do_pick_best((CASE_DECISION(_, vh::CONSTRUCTOR_SIGNATURE (1, 0), _, _)) ! rest, _, n, _, _) =>   n;
            do_pick_best((CASE_DECISION(_, vh::CONSTRUCTOR_SIGNATURE (0, 1), _, _)) ! rest, _, n, _, _) =>   n;

            do_pick_best (a_case ! rest, active as act1 ! _, n, NULL, NULL)
                =>
                if (relevent (a_case, act1))
                    #                    
                    do_pick_best (rest, active, n + 1, THE (metric a_case), THE n);
                else 
                    do_pick_best (rest, active, n + 1, NULL, NULL);
                fi;

            do_pick_best (a_case ! rest, active as act1 ! _, n, THE m, THE i)
                =>
                if (relevent (a_case, act1))
                    #                    
                    my_metric =   metric a_case;

                    if (metric_better (my_metric, m))
                        #
                        do_pick_best (rest, active, n + 1, THE (my_metric), THE n);
                    else 
                        do_pick_best (rest, active, n + 1, THE m,          THE i);
                    fi;

                else 
                    do_pick_best (rest, active, n + 1, THE m, THE i);
                fi;

            do_pick_best _
                =>
                bug "bug situation in do_pick_best";
        end;
        #
        fun pick_best (l, active)
            =
            do_pick_best (l, active, 0, NULL, NULL);

        #
        fun extract_nth (0, a ! b)
               =>
               (a, b);

            extract_nth (n, a ! b)
                => 
                {   (extract_nth (n - 1, b))
                        ->
                        (c, d);

                    (c,  a ! d);
                };

            extract_nth _ =>   bug "extract_nth called with too big n";
        end;

        #
        fun filter (f, NIL)
                =>
                NIL;

            filter (f, a ! b)
                =>
                if (f a)  a ! (filter (f, b));
                else           filter (f, b) ;
                fi;
        end;
        #
        fun make_decision_tree ((decisions, delayed), active as active1 ! _)
                =>
                case (extract_nth (pick_best (decisions, active), decisions))
                  
                     (BIND_DECISION (path, _), rest)
                         =>
                         make_decision_tree (fire_constraint (path, delayed, rest, NIL), active);

#                    (CASE_DECISION (path, vh::CONSTRUCTOR_SIGNATURE (1, 0), 
#                      [(_, _, guarded)], defaults), rest)
#                        => 
#                        make_decision_tree((rest@guarded, delayed), active)
#
#                    (CASE_DECISION (path, vh::CONSTRUCTOR_SIGNATURE (0, 1), 
#                      [(_, _, guarded)], defaults), rest)
#                        => 
#                        make_decision_tree((rest@guarded, delayed), active)

                     (CASE_DECISION (path, an_api, cases, defaults), rest)
                         =>
                         {   fun is_active (_, rules, _)
                                 =
                                 intersect (rules, active)   !=   [];

                             active_cases = filter (is_active, cases);

                             case_trees
                                 = 
                                 make_cases (active_cases, rest, delayed, defaults, active);

                             def_active
                                 =
                                 intersect (active, defaults);
                             #
                             fun len (vh::CONSTRUCTOR_SIGNATURE (i, j)) =>   i+j;
                                 len (vh::NULLARY_CONSTRUCTOR         ) =>   0;
                             end;

                             def_tree
                                 = 
                                 if (length active_cases == len an_api)
                                      NULL; 
                                 else THE (make_decision_tree((rest, delayed), def_active));
                                 fi;

                             plj::CASETEST (path, an_api, case_trees, def_tree);
                         };

                     (ABSCON_DECISION (path, con, yes, guarded, defaults), rest)
                         =>
                         {   yes_active =   intersect (active, union (yes, defaults));
                             no_active  =   intersect (active, defaults);

                             yes_tree =   make_decision_tree((rest@guarded, delayed), yes_active);
                             def_tree =   make_decision_tree((rest, delayed), no_active);

                             if (plj::unary con)   plj::ABSTEST1 (path, con, yes_tree, def_tree);
                             else                  plj::ABSTEST0 (path, con, yes_tree, def_tree);
                             fi;
                         };

                     esac
                     except
                         PICK_BEST =  plj::RHS active1;

            make_decision_tree (_, active)
                =>
                bug "nothing active";
        end 



        also
        fun make_cases (NIL, decs, delayed, defaults, active)
                =>
                NIL;

            make_cases ((pcon, rules, guarded) ! rest, decs, delayed, defaults, active)
                => 
                {   r_active = intersect (union (defaults, rules), active);

                    (pcon, make_decision_tree((decs@guarded, delayed), r_active))
                    !
                    (make_cases (rest, decs, delayed, defaults, active));
                };
        end;



        stipulate
            include package   print_junk;
            #
            print_depth =   global_controls::print::print_depth;
        herein
            #
            fun match_print (dictionary, rules, unused) pp
                =
                {   fun match_print' ([], _, _)
                            =>
                            ();

                        match_print' ([(pattern, _)], _, _)
                            =>
                            ();   #  never print last rule 

                        match_print' ((pattern, _) ! more,[], _)
                            =>
                            {   pp.lit "        "; 
                                unparse_deep_syntax::unparse_pattern dictionary pp (pattern,*print_depth);
                                pp.lit " => ...";
                                pp.newline();
                                match_print' (more,[], 0);
                            };

                        match_print' ((pattern, _) ! more, (taglist as (tag ! tags)), i)
                            =>
                            if   (i == tag) 
                                
                                 pp.lit "  -->   ";
                                 unparse_deep_syntax::unparse_pattern dictionary pp (pattern,*print_depth);
                                 pp.lit " => ..."; 
                                 pp.newline();
                                 match_print'(more, tags, i+1);
                            else 
                                 pp.lit "        ";
                                 unparse_deep_syntax::unparse_pattern dictionary pp (pattern,*print_depth);
                                 pp.lit " => ...";
                                 pp.newline();
                                 match_print'(more, taglist, i+1);
                            fi;
                    end;

                    pp.newline();
                    pp.box {.                                                           pp.rulename "tds1";
                        match_print'(rules, unused, 0);
                    };
                };
            #
            fun bind_print (dictionary, (pattern, _) ! _) pp
                    =>
                    {   pp.newline();
                        pp.lit "        "; 
                        unparse_deep_syntax::unparse_pattern dictionary pp (pattern,*print_depth);
                        pp.lit " = ...";
                    };

                bind_print _ _
                    =>
                    bug "bind_print in mc";
            end;

        end;                            # stipulate printutil 


        #
        fun rules_used (plj::RHS n)
                =>
                [n];

            rules_used (plj::BIND(_, dt))
                =>
                rules_used dt;

            rules_used (plj::CASETEST(_, _, cases, NULL))
                =>
                fold_backward
                    (\\((_, a), b) = union (rules_used a, b))
                    NIL
                    cases;

            rules_used (plj::CASETEST(_, _, cases, THE dt))
                =>
                fold_backward
                    (\\((_, a), b) = union (rules_used a, b))
                    (rules_used dt)
                    cases;

            rules_used (plj::ABSTEST0(_, _, yes, no))
                => 
                union (rules_used yes, rules_used no);

            rules_used (plj::ABSTEST1(_, _, yes, no))
                => 
                union (rules_used yes, rules_used no);
        end;


        #
        fun fix_up_unused (NIL, _, _, _, out)
                =>
                out;

            fix_up_unused (unused, (NIL, _) ! rest, n, m, out)
                => 
                fix_up_unused (unused, rest, n, m + 1, out);

            fix_up_unused (unused ! urest, (rule ! rules, x) ! mrest, n, m, NIL)
                =>
                if   (unused == n)
                     
                     fix_up_unused (urest, (rules, x) ! mrest, n + 1, m, [m]);
                else 
                     fix_up_unused (unused ! urest, (rules, x) ! mrest, n + 1, m, NIL);
                fi;

            fix_up_unused (unused ! urest, (rule ! rules, z) ! mrest, n, m, x ! y)
                 =>
                 if (unused == n)
                     
                     if (m != x)
                          fix_up_unused (urest, (rules, z) ! mrest, n + 1, m, m ! x ! y);
                     else fix_up_unused (urest, (rules, z) ! mrest, n + 1, m, x ! y    );
                     fi;
                 else
                     fix_up_unused (unused ! urest, (rules, z) ! mrest, n + 1, m, x ! y);
                 fi;

            fix_up_unused _
                =>
                bug "bad fixup";
        end;

        #
        fun redundant (NIL, n: Int)
                =>
                FALSE;

            redundant (a ! b, n)
                =>
                a != n
                or
                redundant (b, n);
        end;

        #
        fun complement (n, m, a ! b)
                =>
                n < a   ??   n ! (complement (n + 1, m, a ! b))
                        ::        complement (n + 1, m,     b );

            complement (n, m, NIL)
                => 
                n < m   ??   n ! (complement (n + 1, m, NIL))
                        ::                              NIL  ;
        end;

        #
        fun divide_path_list (prior, NIL, accyes, accno) 
                =>
                (accyes, accno);

            divide_path_list (prior, path ! rest, accyes, accno)
                => 
                prior path  ??   divide_path_list (prior, rest, path ! accyes, accno)
                            ::   divide_path_list (prior, rest, accyes, path ! accno);
        end;

        #
        fun add_path_to_path_list (path, path1 ! rest)
                => 
                plj::path_eq (path, path1)
                    ??   path1 ! rest
                    ::   path1 ! (add_path_to_path_list (path, rest));

            add_path_to_path_list (path, NIL)
                =>
                [ path ];
        end;

        #
        fun unite_path_lists (paths1, NIL) =>   paths1;
            unite_path_lists (NIL, paths2) =>   paths2;

            unite_path_lists (path1 ! rest1, paths2)
                => 
                add_path_to_path_list (path1, unite_path_lists (rest1, paths2));
        end;

        #
        fun on_path_list (path1, NIL)
                =>
                FALSE;

            on_path_list (path1, path2 ! rest)
                => 
                plj::path_eq (path1, path2) or on_path_list (path1, rest);
        end;

        #
        fun intersect_path_lists (paths1, NIL) =>   NIL;
            intersect_path_lists (NIL, paths2) =>   NIL;

            intersect_path_lists (path1 ! rest1, paths2)
                => 
                on_path_list (path1, paths2)
                    ??  path1 ! (intersect_path_lists (rest1, paths2))
                    ::  intersect_path_lists (rest1, paths2);
        end;

        #
        fun difference_path_lists (paths1, NIL) =>   paths1;
            difference_path_lists (NIL, paths2) =>   NIL;

            difference_path_lists (path1 ! rest1, paths2)
                => 
                on_path_list (path1, paths2)
                    ??         (difference_path_lists (rest1, paths2))
                    :: path1 ! (difference_path_lists (rest1, paths2));
        end;
        #
        fun intersect_pathsets (pathset1, NIL) =>   NIL;
            intersect_pathsets (NIL, pathset2) =>   NIL;

            intersect_pathsets (pathset1 as (n1: Int, paths1) ! rest1, 
                                pathset2 as (n2,      paths2) ! rest2
                               )
                =>
                if (n1 == n2)
                    #
                    case (intersect_path_lists (paths1, paths2))
                        #
                        NIL =>  intersect_pathsets (rest1, rest2);
                        pl  =>  (n1, pl) ! (intersect_pathsets (rest1, rest2));
                    esac;

                elif (n1 < n2) 
                     intersect_pathsets (rest1, pathset2);
                else intersect_pathsets (pathset1, rest2);
                fi;
        end;
        #
        fun unite_pathsets (pathset1, NIL) =>   pathset1;
            unite_pathsets (NIL, pathset2) =>   pathset2;

            unite_pathsets (pathset1 as (n1: Int, paths1) ! rest1, 
                            pathset2 as (n2,      paths2) ! rest2
                           )
                =>
                if (n1 == n2)
                    #                
                    (n1, unite_path_lists (paths1, paths2))
                    !  (unite_pathsets (rest1, rest2));
                else
                    if (n1 < n2)   (n1, paths1) ! (unite_pathsets (rest1, pathset2));
                    else           (n2, paths2) ! (unite_pathsets (pathset1, rest2));
                    fi;
                fi;
        end;
        #
        fun difference_pathsets (pathset1, NIL) =>   pathset1;
            difference_pathsets (NIL, pathset2) =>   NIL;

            difference_pathsets (pathset1 as (n1: Int, paths1) ! rest1, 
                               pathset2 as (n2, paths2) ! rest2)
                =>
                if (n1 == n2)
                    #                     
                    case (difference_path_lists (paths1, paths2))
                        #
                        NIL =>  difference_pathsets (rest1, rest2);
                        pl  =>  (n1, pl) ! (difference_pathsets (rest1, rest2));
                    esac;
                else
                    if   (n1 < n2)
                         (n1, paths1) ! (difference_pathsets (rest1, pathset2));
                    else difference_pathsets (pathset1, rest2);
                    fi;
                fi;
        end;
        #
        fun do_pathset_member (path, metric, (n: Int, paths) ! rest)
                =>
                (n < metric and do_pathset_member (path, metric, rest))
                or
                (n == metric and on_path_list (path, paths));

            do_pathset_member (path, metric, NIL) => FALSE;
        end;

        #
        fun do_add_element_to_pathset (path, metric, NIL)
                =>
                [ (metric, [ path ] ) ];

            do_add_element_to_pathset (path, metric, (n: Int, paths) ! rest)
                =>
                if (n == metric)
                    #
                    (n, add_path_to_path_list (path, paths)) ! rest;

                elif (n < metric)
                    #
                    (n, paths) ! (do_add_element_to_pathset (path, metric, rest));
                else
                    (metric, [path]) ! (n, paths) ! rest;
                fi;
        end;
        #
        fun divide_path_set (prior, NIL)
                =>
                (NIL, NIL);

            divide_path_set (prior, (n, pathlist) ! rest)
                =>
                {   (divide_path_set (prior, rest))
                        ->
                        (yes_set, no_set);

                    case (divide_path_list (prior, pathlist, NIL, NIL) )
                        #
                        (NIL, NIL) =>  bug "paths dissappeared during divide";
                        (NIL, no ) =>  (yes_set, (n, no) ! no_set);
                        (yes, NIL) =>  ((n, yes) ! yes_set, no_set);
                        (yes, no ) =>  ((n, yes) ! yes_set, (n, no) ! no_set);
                    esac;
                };
        end;
        #
        fun path_depends path1 plj::ROOT_PATH
                =>
                plj::path_eq (path1, plj::ROOT_PATH);

            path_depends path1 (path2 as plj::RECORD_PATH paths)
                => 
                fold_forward
                    (\\ (a, b) = (path_depends path1 a) or b) 
                    (plj::path_eq (path1, path2))
                    paths; 

            path_depends path1 (path2 as plj::PI_PATH(_, subpath))
                =>
                plj::path_eq (path1, path2) or path_depends path1 subpath;      

            path_depends path1 (path2 as plj::VPI_PATH(_, _, subpath))
                =>
                plj::path_eq (path1, path2) or path_depends path1 subpath;      

            path_depends path1 (path2 as plj::DELTA_PATH(_, subpath))
                =>
                plj::path_eq (path1, path2) or path_depends path1 subpath;

            path_depends path1 (path2 as (plj::VLEN_PATH (subpath, _)))
                =>
                plj::path_eq (path1, path2) or path_depends path1 subpath;
        end;

        #
        fun path_metric plj::ROOT_PATH
                =>
                0;

            path_metric (plj::RECORD_PATH paths)
                =>
                fold_backward
                    (\\ (a, b) = path_metric a + b)
                    1
                    paths;

            path_metric (plj::PI_PATH(_, subpath))
                =>
                1 + path_metric subpath;

            path_metric (plj::VPI_PATH(_, _, subpath))
                =>
                1 + path_metric subpath;

            path_metric (plj::DELTA_PATH(_, subpath))
                =>
                1 + path_metric subpath;

            path_metric (plj::VLEN_PATH (subpath, _))
                =>
                1 + path_metric subpath;
        end;

        #
        fun pathset_member path pathset
            = 
            do_pathset_member (path, path_metric path, pathset);

        #
        fun add_path_to_pathset (path, pathset)
            =
            do_add_element_to_pathset (path, path_metric path, pathset); 

        #
        fun do_do_namings (NIL, rhs)
                =>
                rhs;

            do_do_namings (path ! rest, rhs)
                =>
                plj::BIND (path, do_do_namings (rest, rhs));
        end;

        #
        fun do_namings (NIL, rhs)
                =>
                rhs;

            do_namings ((n, paths) ! morepaths, rhs)
                => 
                do_do_namings (paths, do_namings (morepaths, rhs));
        end;

        #
        fun sub_paths plj::ROOT_PATH
                =>
                [ (0, [ plj::ROOT_PATH ] ) ];

            sub_paths (path as plj::RECORD_PATH paths)
                =>
                fold_backward unite_pathsets [(path_metric path, [path])] (map sub_paths paths);

            sub_paths (path as (plj::VLEN_PATH (subpath, _)))
                =>
                (sub_paths subpath) @ [(path_metric path, [path])];

            sub_paths (path as plj::VPI_PATH (n, _, subpath))
                =>
                (sub_paths subpath) @ [(path_metric path, [path])];

            sub_paths (path as plj::PI_PATH (n, subpath))
                =>
                (sub_paths subpath) @ [(path_metric path, [path])];

            sub_paths (path as plj::DELTA_PATH (_, subpath))
                =>
                (sub_paths subpath) @ [(path_metric path, [path])];
        end;

        #
        fun rhs_namings (n, rule_desc)
            = 
            {   (list::nth (rule_desc, n))
                    ->
                    (_, paths, _);

                fold_backward unite_pathsets [] (map sub_paths paths);
            };
        #
        fun pass1_cases ((pcon, subtree) ! rest, envin, THE envout, rhs, path)
                =>
                {   (pass1 (subtree, envin, rhs))
                        ->
                        (subtree', my_env_out);

                    (divide_path_set (path_depends (plj::DELTA_PATH (pcon, path)), my_env_out))
                        ->
                        (must_bind_here, other_namings);

                    env_out_so_far =   intersect_pathsets (envout, other_namings);

                    (pass1_cases (rest, envin, THE env_out_so_far, rhs, path))
                        ->
                        (rest', envout');

                    i_bind2   =   difference_pathsets  (other_namings,  envout');

                    subtree'' =   do_namings (unite_pathsets (must_bind_here, i_bind2), subtree');

                    ((pcon, subtree'') ! rest', envout');
                };

            pass1_cases((pcon, subtree) ! rest, envin, NULL, rhs, path)
                =>
                {   (pass1 (subtree, envin, rhs))
                        ->
                        (subtree', my_env_out);

                    (divide_path_set (path_depends (plj::DELTA_PATH (pcon, path)), my_env_out))
                        ->
                        (must_bind_here, other_namings);

                    (pass1_cases (rest, envin, THE other_namings, rhs, path))
                        ->
                        (rest', envout');

                    i_bind2   =   difference_pathsets (other_namings, envout');

                    subtree'' =   do_namings (unite_pathsets (must_bind_here, i_bind2), subtree');

                    ((pcon, subtree'') ! rest', envout');
                };

            pass1_cases (NIL, envin, THE envout, rhs, path)
                =>
                (NIL, unite_pathsets (envin, envout));

            pass1_cases (NIL, envin, NULL, rhs, path)
                =>
                bug "pass1_cases bad";
        end 

        also
        fun pass1 (plj::RHS n, envin, rhs)
                =>
                (plj::RHS n, rhs_namings (n, rhs));

            pass1 (plj::CASETEST (path, an_api, cases, NULL), envin, rhs)
                =>
                {   my  (cases', envout')
                        =
                        pass1_cases (cases, unite_pathsets (envin, sub_paths path), 
                                 NULL, rhs, path);

                    (plj::CASETEST (path, an_api, cases', NULL), envout');
                };

            pass1 (plj::CASETEST (path, an_api, cases, THE subtree), envin, rhs)
                =>
                {   new_dictionary =   unite_pathsets (envin, sub_paths path);
                    #
                    (pass1 (subtree, new_dictionary, rhs))
                        ->
                        (subtree', sub_envout);

                    (pass1_cases (cases, new_dictionary, THE sub_envout, rhs, path))
                        ->
                        (cases', envout');

                    subnamings =   difference_pathsets (sub_envout, envout');
                    subtree''   =   do_namings (subnamings, subtree');

                    (plj::CASETEST (path, an_api, cases', THE subtree''), envout');
                };

            pass1 (plj::ABSTEST0 (path, con, subtree1, subtree2), envin, rhs)
                =>
                {   new_dictionary = unite_pathsets (envin, sub_paths path);

                    my (subtree1', sub_envout1) = pass1 (subtree1, new_dictionary, rhs);
                    my (subtree2', sub_envout2) = pass1 (subtree2, new_dictionary, rhs);

                    envout =   unite_pathsets (new_dictionary, intersect_pathsets (sub_envout1, sub_envout2));

                    bind1 = difference_pathsets (sub_envout1, envout);
                    bind2 = difference_pathsets (sub_envout2, envout);

                    subtree1'' = do_namings (bind1, subtree1');
                    subtree2'' = do_namings (bind2, subtree2');

                    (plj::ABSTEST0 (path, con, subtree1'', subtree2''), envout);
                };

            pass1 (plj::ABSTEST1 (path, con, subtree1, subtree2), envin, rhs)
                =>
                {   new_dictionary =   unite_pathsets (envin, sub_paths path);

                    yesenv =    if (plj::is_an_exception con)   new_dictionary;
                                else                            add_path_to_pathset (plj::DELTA_PATH (plj::DATAPCON con, path), envin);
                                fi;

                    my (subtree1', sub_envout1) =   pass1 (subtree1, yesenv,         rhs);
                    my (subtree2', sub_envout2) =   pass1 (subtree2, new_dictionary, rhs);

                    envout =    unite_pathsets (new_dictionary,
                                                intersect_pathsets (sub_envout1, sub_envout2));

                    bind1 =   difference_pathsets (sub_envout1, envout);
                    bind2 =   difference_pathsets (sub_envout2, envout);

                    subtree1'' =   do_namings (bind1, subtree1');
                    subtree2'' =   do_namings (bind2, subtree2');

                    (plj::ABSTEST1 (path, con, subtree1'', subtree2''), envout);
                };

            pass1 _
                =>
                bug "pass1 bad";
        end;



        # Given a decision tree for a match,
        # a list of ?? and the name of the 
        # variable bound to the value to be
        # matched, produce code for the match. 
        #
        fun make_match_code (dt, match_rep, root_variable, (to_type, to_lambda_type), giis)
            = 
            {   (pass1 (dt, [(0, [plj::ROOT_PATH])], match_rep))
                    ->
                    (subtree, envout);
                    
                #
                fun make_sumtype (tdt::VALCON { name, form, typoid, ... } )
                    = 
                    ( name,
                      form,
                      to_valcon_lty  to_lambda_type  typoid
                    );

                #
                fun make_path (plj::RECORD_PATH paths, dictionary)
                        =>
                        lcf::RECORD (map (\\ path =  lcf::VAR (plj::get_path (path, dictionary)))  paths);

                    make_path (plj::PI_PATH (n, path), dictionary)
                          => 
                          lcf::GET_FIELD (n, lcf::VAR (plj::get_path (path, dictionary)));

                    make_path (p as plj::DELTA_PATH (pcon, path), dictionary)
                        => 
                        lcf::VAR (plj::get_path (p, dictionary));

                    make_path (plj::VPI_PATH (n, t, path), dictionary)
                        =>
                        {   tc = to_type t;
                            #
                            lt_sub
                                = 
                                {   x =   hcf::make_ro_vector_uniqtypoid (hcf::make_typevar_i_uniqtypoid 0);
                                    #
                                    hcf::make_typeagnostic_uniqtypoid
                                      (
                                        [ hcf::plaintype_uniqkind ], 
                                        [ hcf::make_lambdacode_arrow_uniqtypoid (hcf::make_tuple_uniqtypoid [x, hcf::int_uniqtypoid], hcf::make_typevar_i_uniqtypoid 0) ]
                                      );
                                };

                            lcf::APPLY (lcf::BASEOP (hbo::RO_VECTOR_GET, lt_sub, [tc]),
                                lcf::RECORD [lcf::VAR (plj::get_path (path, dictionary)), lcf::INT n]);
                        };

                    make_path (plj::VLEN_PATH (path, t), dictionary)
                        => 
                        {   tc =  to_type t;
                            #
                            lt_len =  hcf::make_typeagnostic_uniqtypoid([hcf::plaintype_uniqkind], 
                                             [hcf::make_lambdacode_arrow_uniqtypoid (hcf::make_typevar_i_uniqtypoid 0, hcf::int_uniqtypoid)]);

                            argtc =  hcf::make_ro_vector_uniqtype  tc;

                            lcf::APPLY (lcf::BASEOP (hbo::VECTOR_LENGTH_IN_SLOTS, lt_len, [argtc]), 
                                lcf::VAR (plj::get_path (path, dictionary)));
                        };

                    make_path (plj::ROOT_PATH, dictionary)
                        =>
                        lcf::VAR (plj::get_path (plj::ROOT_PATH, dictionary));
                end;

                #
                fun make_switch (sv, an_api, [(lcf::VAL_CASETAG((_, vh::REFCELL_REP, lt), ts, x), e)], NULL)
                        => 
                        lcf::LET (x, lcf::APPLY (lcf::BASEOP (hbo::GET_REFCELL_CONTENTS, hcf::lt_swap lt, ts), sv), e);

                    make_switch (sv, an_api, [(lcf::VAL_CASETAG((_, vh::SUSPENSION (THE(_, vh::HIGHCODE_VARIABLE f)), lt),
                                                  ts, x), e)], NULL)
                        => 
                        {   v = make_var();
                            #
                            lcf::LET (x, lcf::LET (v, lcf::APPLY_TYPEFUN (lcf::VAR f, ts), lcf::APPLY (lcf::VAR v, sv)), e);
                        };

                    make_switch (sv, an_api, cases as ((lcf::INTEGER_CASETAG _, _) ! _), default)
                        =>
                        case default
                            #
                            THE d =>  giis (sv, map strip cases, d);
                            NULL  =>  bug "no default in switch on INTEGER";
                        esac
                        where
                            fun strip (lcf::INTEGER_CASETAG n, e) =>   (n, e);
                               strip _                            =>   bug "make_switch: INTEGERCON";
                            end;
                        end;

                    make_switch x
                        =>
                        lcf::SWITCH x;
                end;


                #
                fun pass2rhs (n, dictionary, rule_desc)
                    = 
                    case (list::nth (rule_desc, n))
                        #                     
                        (_, [path], fname)
                            =>
                            lcf::APPLY (lcf::VAR fname, lcf::VAR (plj::get_path (path, dictionary)));

                        (_, paths, fname)
                            =>
                            lcf::APPLY (lcf::VAR fname, 
                                lcf::RECORD (map (\\ path =  lcf::VAR (plj::get_path (path, dictionary)))
                                            paths));
                    esac;


                #
                fun pass2 (plj::BIND (plj::DELTA_PATH _, subtree), dictionary, rhs)
                        => 
                        pass2 (subtree, dictionary, rhs);

                        # We no longer generate explicit DECON, instead,
                        # we add a naming at each switch case.

                    pass2 (plj::BIND (path, subtree), dictionary, rhs)
                        =>
                        {   new_var = make_var();
                            subcode = pass2 (subtree, (path, new_var) ! dictionary, rhs);

                            lcf::LET (new_var, make_path (path, dictionary), subcode);
                        };

                    pass2 (plj::CASETEST (path, an_api, [], NULL), _, _)
                        => 
                        bug "unexpected empty cases in matchcomp";

                    pass2 (plj::CASETEST (path, an_api, [], THE subtree), dictionary, rhs)
                        => 
                        pass2 (subtree, dictionary, rhs);

                    pass2 (plj::CASETEST (path, an_api, cases, dft), dictionary, rhs)
                        => 
                        {   sv =  lcf::VAR (plj::get_path (path, dictionary));
                            #
                            make_switch
                              ( sv,
                                an_api,
                                pass2cases (path, cases, dictionary, rhs), 
                                case dft 
                                    THE subtree => THE (pass2 (subtree, dictionary, rhs));
                                    NULL => NULL;
                                esac
                              );
                        };

                    pass2 (plj::ABSTEST0 (path, con as (dc, _), yes, no), dictionary, rhs)
                        =>
#                       if (is_an_exception con)
#
#                           make_switch (VAR (plj::get_path (path, dictionary)), vh::NULLARY_CONSTRUCTOR, 
#                                      [(VALCON (make_sumtype dc),  pass2 (yes, dictionary, rhs))],
#                                      THE (pass2 (no, dictionary, rhs)))
#                       else
                        abstest0 (path, con, pass2 (yes, dictionary, rhs), pass2 (no, dictionary, rhs)); 

                    pass2 (plj::ABSTEST1 (path, con as (dc, _), yes, no), dictionary, rhs)
                          =>
#                         if is_an_exception con 
#
#                             make_switch (VAR (plj::get_path (path, dictionary)), vh::NULLARY_CONSTRUCTOR,
#                                        [(VALCON (make_sumtype dc),  pass2 (yes, dictionary, rhs))],
#                                        THE (pass2 (no, dictionary, rhs)))
#                         else
                          abstest1 (path, con, pass2 (yes, dictionary, rhs), pass2 (no, dictionary, rhs)); 

                    pass2 (plj::RHS n, dictionary, rhs)
                        =>
                        pass2rhs (n, dictionary, rhs);
                end   

                also
                fun pass2cases (path, NIL, dictionary, rhs)
                        =>
                        NIL;

                    pass2cases (path, (pcon, subtree) ! rest, dictionary, rhs)
                        => 
                        {   # Always implicitly bind a new variable at each branch. 

                            (pcon_to_con (pcon, path, dictionary))
                                ->
                                (ncon, nenv);

                            result =  (ncon, pass2 (subtree, nenv, rhs));

                            result ! (pass2cases (path, rest, dictionary, rhs));
                        };
                end 

                also
                fun pcon_to_con (pcon, path, dictionary)
                    =
                    case pcon
                        #                     
                        plj::DATAPCON (dc, ts)
                            => 
                            {   new_var = make_var();
                                nts = map to_type ts;
                                nenv = (plj::DELTA_PATH (pcon, path), new_var) ! dictionary;

                                (lcf::VAL_CASETAG (make_sumtype dc, nts, new_var), nenv);
                            };

                        plj::VLENPCON (i, t) =>   (lcf::VLEN_CASETAG    i, dictionary);
                        plj::INTPCON     i   =>   (lcf::INT_CASETAG     i, dictionary);
                        plj::INT1PCON    i   =>   (lcf::INT1_CASETAG    i, dictionary);
                        plj::INTEGERPCON n   =>   (lcf::INTEGER_CASETAG n, dictionary);
                        plj::UNTPCON    w    =>   (lcf::UNT_CASETAG     w, dictionary);
                        plj::UNT1PCON   w    =>   (lcf::UNT1_CASETAG    w, dictionary);
                        plj::REALPCON   r    =>   (lcf::FLOAT64_CASETAG r, dictionary);
                        plj::STRINGPCON s    =>   (lcf::STRING_CASETAG  s, dictionary);
                    esac;

                case (do_namings (envout, subtree))
                    #             
                    plj::BIND (plj::ROOT_PATH, subtree')
                        => 
                        pass2 (subtree', [(plj::ROOT_PATH, root_variable)], match_rep);

                    _ => pass2 (subtree, [], match_rep);
                esac;
            };
        #
        fun compile_pattern_match (rules, finish, rootvar, to_tc_lt as (_, to_lambda_type), err, giis)
            =
            {   last_rule  =   length rules - 1;
                match_reps =   map (preprocess_pattern to_lambda_type) rules;

                my  (match_rep, rhs_rep)
                    = 
                    fold_backward
                        (\\ ((a, b), (c, d)) = (a@c, b ! d))
                        ([], [])
                        match_reps;

                all_rules =   make_all_rules (match_rep, 0);
                flattened =   flatten_and_ors (make_and_or (match_rep, err), all_rules);

                ready =   fire_constraint (plj::ROOT_PATH, flattened, NIL, NIL);
                dt    =   make_decision_tree (ready, all_rules);

                rule_count       =   length match_rep;
                raw_unused_rules =   complement (0, rule_count, rules_used dt);
                unused_rules     =   reverse (fix_up_unused (raw_unused_rules, match_reps, 0, 0, NIL));

                exhaustive =   is_there (last_rule, unused_rules);
                redundant_flag =   redundant (unused_rules, last_rule);
                #
                fun g ((fname, fbody), body)
                    =
                    lcf::LET (fname, fbody, body);

                code =   fold_backward
                             g
                             (make_match_code (dt, match_rep, rootvar, to_tc_lt, giis))
                             rhs_rep;

                (finish (code), unused_rules, redundant_flag, exhaustive);
            };


        # Test pattern, the guard pattern of the first match rule of a match,
        # for the occurence of variables (including layering variables) 
        # or wildcards.  Return TRUE if any are present, FALSE otherwise.
        #
        fun no_vars_in ((pattern, _) ! _)
                =>
                not (var pattern)
                where
                    fun var ds::WILDCARD_PATTERN => TRUE; #  might want to flag this 
                        var (ds::VARIABLE_IN_PATTERN _) => TRUE;
                        var (ds::AS_PATTERN _) => TRUE;
                        var (ds::TYPE_CONSTRAINT_PATTERN (p, _)) => var p;
                        var (ds::APPLY_PATTERN(_, _, p)) => var p;
                        var (ds::RECORD_PATTERN { fields, ... } ) => list::exists (var o #2) fields;
                        var (ds::VECTOR_PATTERN (pats, _)) => list::exists var pats;
                        var (ds::OR_PATTERN (pattern1, pattern2)) => var pattern1 or var pattern2;
                        var _ => FALSE;
                    end;
                end;

            no_vars_in _
                =>
                bug "no_vars_in in mc";
        end;



        # The three entry points for the match compiler.
        #
        # They take as arguments a dictionary; a match represented
        # as a list of pattern--lambda expression pairs (weak); and a 
        # function to use in printing warning messages (warn).
        #
        # dictionary and warn are only used in the printing of diagnostic information.
        #
        # If the control flag controls::mc::print_args is set, they print match.  
        #  
        # They call compile_pattern_match to actually compile match.
        # This returns a 4-tuple (code, unused, redundant, exhaustive):
        #    'code' is lambda code that implements match.
        #    'unused' is a list of the indices of the unused rules.
        #    'redundant'  and 'exhaustive' are boolean flags which are
        #         set if  match is redundant or exhaustive respectively.
        #
        # They print warning messages as appropriate, as described below.
        # If the control flag controls::mc::print_ret is set, they print code.
        #
        # They return code.
        #
        # They assume that match has one element for each rule of the match 
        # to be compiled, in order, plus a single, additional, final element.
        # This element must have a pattern that is always matched 
        # (in practice, it is either a variable or wildcard), and a
        # lambda expression that implements the appropriate behavior 
        # for argument values that satisfy none of the guard patterns.
        # A pattern is exhaustive if this dummy rule is never used,
        # and is irredundant if all of the other rules are used.


        stipulate
            include package   global_controls::mc;       # Make various control flags visible 
        herein      

        # Entry point for compiling matches induced by my declarations
        # (e.g., my listHead ! listTail = list).  match is a two 
        # element list.  If the control flag global_controls::mc::warn_on_nonexhaustive_bind
        # is set, and match is nonexhaustive a warning is printed.  If the control
        # flag global_controls::mc::bind_no_variable_warn is set, and the first pattern
        # (i.e., the only non-dummy pattern) of match contains no variables or 
        # wildcards, a warning is printed.    Arguably, a pattern containing no 
        # variables, but one or more wildcards, should also trigger a warning, 
        # but this would cause warnings on constructions like
        # my _ = <expression>  and  my _:<type> = <expression>.
        #
        fun compile_naming_pattern (dictionary, rules, finish, rootv, to_tc_lt, err, giis)
            =
            code
            where
                                                                                                                                if *print_args
                                                                                                                                    pp::with_standard_prettyprinter
                                                                                                                                        (err::default_plaint_sink ())   []
                                                                                                                                        (\\ pp:   pp::Prettyprinter
                                                                                                                                            =
                                                                                                                                            {   pp.lit "MC called with:";
                                                                                                                                                pp.newline();
                                                                                                                                                mp::print_match pp dictionary rules;
                                                                                                                                                pp.newline();
                                                                                                                                                pp.flush();
                                                                                                                                            }
                                                                                                                                        );
                                                                                                                                fi;
                (compile_pattern_match (rules, finish, rootv, to_tc_lt, err, giis))
                    ->
                    (code, _, _, exhaustive);

                nonexhaustive
                    =
                    not exhaustive and
                    (*warn_on_nonexhaustive_bind or *error_on_nonexhaustive_bind);

                no_vars =   *bind_no_variable_warn  and  no_vars_in  rules;

                if nonexhaustive
                    #
                    err if *error_on_nonexhaustive_bind  err::ERROR;
                        else                             err::WARNING;
                        fi

                        ("cases not exhaustive"
                        + (no_vars ?? " and contains no variables" :: "")
                        )
                        (bind_print (dictionary, rules));
                else
                    if no_vars
                         err err::WARNING "naming contains no variables" 
                             (bind_print (dictionary, rules));
                    fi;
                fi;

                if *print_ret
                    pp::with_standard_prettyprinter
                        (err::default_plaint_sink ())   []
                        (\\ pp:   pp::Prettyprinter
                            =
                            {   pp.lit "MC returns with:";
                                pp.newline();
                                mp::prettyprint_lambdacode_expression  pp  code;
                                pp.newline();
                                pp.flush();
                            }
                        );
                fi;
            end;


        # Entry point for compiling matches induced by exception handlers.
        # (e.g., except BIND => Foo).  If the control flag 
        #  global_controls::mc::warn_on_redundant_match is set, and match is redundant, 
        #  a warning is printed.  If global_controls::mc::error_on_redundant_match is also
        #  set, the warning is promoted to an error message.
        #
        fun compile_exception_pattern (dictionary, rules, finish, rootv, to_tc_lt, err, giis)
            =
            {   if *print_args
                    pp::with_standard_prettyprinter
                        (err::default_plaint_sink ())   []
                        (\\ pp:   pp::Prettyprinter
                            =
                            {   pp.lit "MC called with:";
                                pp.newline();
                                mp::print_match pp dictionary rules;
                                pp.newline();
                                pp.flush();
                            }
                        );
                fi;

                (compile_pattern_match (rules, finish, rootv, to_tc_lt, err, giis))
                    ->
                    (code, unused, redundant, _);

                redundant =   *warn_on_redundant_match and redundant;

                if redundant
                     err 
                       if *error_on_redundant_match  err::ERROR;
                       else                          err::WARNING;
                       fi
                       "redundant patterns in match"
                       (match_print (dictionary, rules, unused));
                fi;

                if *print_ret 
                    pp::with_standard_prettyprinter
                        (err::default_plaint_sink ())   []
                        (\\ pp:   pp::Prettyprinter
                            =
                            {   pp.lit "MC returns with:";
                                pp.newline();
                                mp::prettyprint_lambdacode_expression  pp  code;
                                pp.newline();
                                pp.flush();
                            }
                        );
                fi;

                code;
            };


        # Entry point for compiling matches induced
        # by function expressions, and thus case expressions,
        # if-then-else expressions, while expressions
        # and fun declarations, (e.g., \\ (x ! y) => ([x], y)).
        #
        # If the control flag  global_controls::mc::warn_on_redundant_match is set,
        # and match is redundant, a warning  is printed.
        # If global_controls::mc::error_on_redundant_match is also set,
        # the warning is promoted to an error.
        #
        # If the control flag global_controls::mc::matchExhaustive is set
        # and match is nonexhaustive, a warning is printed.   
        #
        fun compile_case_pattern (dictionary, rules, finish, rootv, to_tc_lt, err, giis)
            =
            code
            where
                if *print_args
                    pp::with_standard_prettyprinter
                        (err::default_plaint_sink ())   []
                        (\\ pp:   pp::Prettyprinter
                            =
                            {   pp.lit "MC called with:";
                                pp.newline();
                                mp::print_match pp dictionary rules;
                                pp.newline();
                                pp.flush();
                            }
                        );
                fi;

                (compile_pattern_match (rules, finish, rootv, to_tc_lt, err, giis))
                    ->
                    (code, unused, redundant, exhaustive);

                nonexhaustive
                    = 
                    not exhaustive
                    and
                    (*error_on_nonexhaustive_match or *warn_on_nonexhaustive_match);

                redundant =   redundant and (*error_on_redundant_match or *warn_on_redundant_match);

                case (nonexhaustive, redundant)
                    #             
                    (TRUE, TRUE)
                        =>
                        err if (*error_on_redundant_match or *error_on_nonexhaustive_match) err::ERROR;
                            else                                                            err::WARNING;
                            fi
                            "match redundant and nonexhaustive"
                            (match_print (dictionary, rules, unused));

                    (TRUE, FALSE)
                        =>
                         err if *error_on_nonexhaustive_match  err::ERROR;
                             else                              err::WARNING;
                             fi
                             "match nonexhaustive"
                             (match_print (dictionary, rules, unused));

                    (FALSE, TRUE)
                        =>
                        err if *error_on_redundant_match   err::ERROR;
                            else                           err::WARNING;
                            fi
                            "match redundant" (match_print (dictionary, rules, unused));

                    _   => ();
                esac;

                if *print_ret
                    pp::with_standard_prettyprinter
                        (err::default_plaint_sink ())   []
                        (\\ pp:   pp::Prettyprinter
                            =
                            {   pp.lit "compile_case_pattern:  returns with";
                                pp.newline();
                                mp::prettyprint_lambdacode_expression  pp  code;
                                pp.newline();
                                pp.flush();
                            }
                        );
                fi;
            end;


        compile_case_pattern
            = 
            cos::do_compiler_phase  (cos::make_compiler_phase "Compiler 045  matchcomp")  compile_case_pattern;

        end;                                                                    # local controls::mc 
    };                                                                          # package translate_deep_syntax_pattern_to_lambdacode 
end;                                                                            # toplevel stipulate 








Comments and suggestions to: bugs@mythryl.org

PreviousUpNext