PreviousUpNext

15.4.427  src/lib/compiler/back/low/tools/match-compiler/match-compiler-g.pkg

# match-compiler-g.pkg
# A pattern matching compiler. 
# This is based on Pettersson's 13p 1992 paper
# ``A Term Pattern-Match Compiler Inspired by Finite Automata Theory''
# ftp://ftp.ida.liu.se/pub/labs/pelab/papers/cc92pmc.ps.gz

# Compiled by:
#     src/lib/compiler/back/low/tools/match-compiler.lib



###               "Concern should drive us into action
###                and not into depression. No man is
###                free who cannot control himself."
###
###                                    -- Pythagoras 



stipulate
    package iht =  int_hashtable;                                                               # int_hashtable                 is from   src/lib/src/int-hashtable.pkg
    package lms =  list_mergesort;                                                              # list_mergesort                is from   src/lib/src/list-mergesort.pkg
    package spp =  simple_prettyprinter;                                                        # simple_prettyprinter          is from   src/lib/prettyprint/simple/simple-prettyprinter.pkg
    #
    sanity_check = TRUE;
    debug        = FALSE;

herein

    # 2008-01-29 CrT:  So far as I can tell, this generic is invoked only by
    #
    #                      src/lib/compiler/back/low/tools/match-compiler/match-gen-g.pkg
    #
    #                  which in turn appears not to be used in the compiler mainline.
    #                  Compiler mainline pattern-match compilation is handled by
    #
    #                       src/lib/compiler/back/top/translate/translate-deep-syntax-pattern-to-lambdacode.pkg
    #
    #                  This version differs from the mainline by supporting guard expressions
    #                  as implemented by   nowhere.pkg   -- see
    #
    #                      src/lib/compiler/back/low/tools/doc/nowhere.tex
    #                      src/lib/compiler/back/low/tools/nowhere/README   

    #
    generic package   match_compiler_g   (
        #             ================
        #
        package var:                                                                            # A variable 
            api {  Var; 
                 compare:  (Var, Var) -> Order; 
                 to_string:  Var -> String;
            };

        package con:                                                                            # Sumtype constructors.
            api {
                Con;
                compare:      (Con, Con) -> Order;
                to_string:     Con -> String;
                variants:     Con -> { known: List( Con ), others: Bool };
                arity:        Con -> Int;
            };  

        package lit:                                                                            # literals 
            api {
                Literal;
                compare:   (Literal, Literal) -> Order;
                to_string:  Literal -> String;
                variants:  Literal -> Null_Or { known: List( Literal ), others: Bool };
            };

        package act:    
            api {  Action;                                                                      # An action.
                 to_string:  Action -> String;
                 free_vars:  Action -> List( var::Var );
            };

        package gua:                                                                            # A guard expression.
            api {  Guard;
                 to_string:    Guard -> String;
                 compare:     (Guard, Guard) -> Order;
                 logical_and:  (Guard, Guard) -> Guard;
            };

        package exp:
            api {  Expression;
                 to_string:  Expression -> String;
            };
    )
    : (weak) Match_Compiler                                                                     # Match_Compiler        is from   src/lib/compiler/back/low/tools/match-compiler/match-compiler.api
    {

        i2s = int::to_string;

        fun listify (l, s, r) list
            = 
            l + list::fold_backward
                    (   \\ (x, "") => x;
                           (x,  y) => x + s + y;
                        end
                    )
                    ""
                    list + r;

        #  paired_lists::all has the wrong semantics! 
        fun forall f ([],     []    ) =>  TRUE;
            forall f (x ! xs, y ! ys) =>  f (x, y) and forall f (xs, ys);
            forall f _                =>  FALSE;
        end;

        Index = INT  Int
              | LABEL  var::Var;

        Path  = PATH  List( Index );

        package index {

           fun compare (INT i,   INT   j) =>  int::compare (i, j);
               compare (LABEL i, LABEL j) =>  var::compare (i, j);
               compare (INT _,   LABEL _) =>  LESS;
               compare (LABEL _, INT   _) =>  GREATER;
           end;

           fun equal (x, y)
               =
               compare (x, y) == EQUAL;

           fun to_string (INT i) => i2s i;
               to_string (LABEL l) => var::to_string l;
           end;
        };

        package path {

           fun compare (PATH p1, PATH p2)
               =
               loop (p1, p2)
               where
                   fun loop ([], []) => EQUAL;
                       loop([], _)  => LESS;
                       loop(_, [])  => GREATER;

                       loop (x ! xs, y ! ys)
                           =>
                           case (index::compare (x, y))

                                EQUAL =>  loop (xs, ys);
                                ord   =>  ord;
                           esac;

                   end;
               end;


           fun equal (p1, p2)
               =
               compare (p1, p2) == EQUAL;


           fun append (PATH p1, PATH p2)
               =
               PATH (p1@p2);


           fun dot (PATH p, i)
               =
               PATH (p @ [i]);


           fun to_string (PATH p)
               =
               "["
               +
               list::fold_backward
                   (\\ (i, "") =>  index::to_string i;
                       (i, s ) =>  index::to_string i  +  "."  +  s;
                    end
                   )
                   ""
                   p
               +
               "]";


           fun to_ident (PATH p)
               = 
               "v_"
               +
               list::fold_backward
                   (\\ (i, "") =>  index::to_string i;
                       (i,  s) =>  index::to_string i + "_" + s;
                    end
                   )
                   ""
                   p;


            package map
                =
                red_black_map_g (                                               # red_black_map_g               is from   src/lib/src/red-black-map-g.pkg
                    Key = Path;
                    compare = compare;
                );
        };

        Name = VAR  var::Var
             | PVAR Path;

        package name {

            fun to_string (VAR  v) =>  var::to_string  v;
                to_string (PVAR p) =>      path::to_string  p;
            end; 

            fun compare (VAR x, VAR y) => var::compare (x, y); 
                compare (PVAR x, PVAR y) => path::compare (x, y); 
                compare (VAR  _, PVAR _) => LESS;
                compare (PVAR  _, VAR _) => GREATER;
            end;

            fun equal (x, y)
                =
                compare (x, y) == EQUAL;

            package set
                =
                red_black_set_g ( Key = Name; compare = compare;);

            fun set_to_string s
                = 
                "{ "
                +
                list::fold_backward
                    (\\ (v, "") =>  to_string v;
                        (v, s ) =>  to_string v + "." + s;
                     end)
                     ""
                     (set::vals_list s)
                +
                " }";
        };

        package var_set
            =
            red_black_set_g (
                Key =  var::Var;
                compare =  var::compare;
            );

        package subst
            =
            red_black_map_g (                                           # red_black_map_g               is from   src/lib/src/red-black-map-g.pkg
                Key = var::Var;
                compare = var::compare;
            );

                                            # red_black_map_g   def in    src/lib/src/red-black-map-g.pkg

        Subst = subst::Map( Name );

        fun merge_subst (s1, s2)
            =
            subst::keyed_fold_backward
                (\\ (k, v, s) =  subst::set (s, k, v))
                s1
                s2;

        # Internal rep of pattern after
        # every variable has been renamed:
        #
        Pattern
            = WILDCARD_PATTERN                                  #  wild card 
            | APPLY_PATTERN  (Decon, List( Pattern ))           #  Constructor 
            | TUPLEPAT  List( Pattern  )                        #  tupling 
            | RECORD_PATTERN  List ((var::Var, Pattern))   #  record 
            | OR_PATTERN  List ((Subst, Pattern))               #  Disjunction 
            | ANDPAT  List ((Subst, Pattern))                   #  conjunction
            | NOTPAT  (Subst, Pattern)                          #  negation 
            | WHEREPAT  (Pattern, Subst, gua::Guard)          #  guard 
            | NESTEDPAT  (Pattern, Subst, Path, ((Int, exp::Expression)), Pattern)
            | CONTPAT  (var::Var, Pattern) 

        also
        Decon = CON  con::Con          
              | LIT  lit::Literal
              ;   

        exception MATCH_COMPILER  String;

        fun error msg =  raise exception MATCH_COMPILER msg; 
        fun bug   msg =  error ("bug: " + msg);

        package con     = con;
        package action  = act;
        package literal = lit;
        package guard   = gua;

        package expression   = exp;
        package variable     = var;

        package decon {

           fun kind (CON _) => 0;
               kind (LIT _) => 1;
           end;

           fun compare (CON x, CON y) =>  con::compare      (x, y);
               compare (LIT x, LIT y) =>  lit::compare  (x, y);
               compare (    x,     y) =>  int::compare (kind x, kind y);
           end;

           fun to_string (CON c) =>      con::to_string  c;
               to_string (LIT l) =>  lit::to_string  l;
           end;

           fun equal (x, y)
               =
               compare (x, y) == EQUAL;

                                                    # red_black_map_g           is from   src/lib/src/red-black-map-g.pkg

           package map =  red_black_map_g ( Key = Decon; compare = compare;);
           package set =  red_black_set_g ( Key = Decon; compare = compare;);

        }; 

        package pattern {
            #
            fun sort_by_label l
                =
                lms::sort_list
                    #  
                    (\\ ((x, _), (y, _)) =  var::compare (x, y) == GREATER)
                    #  
                    l;

            fun to_string (WILDCARD_PATTERN) => "_";
                to_string (APPLY_PATTERN (c,[])) => decon::to_string c;

                to_string (APPLY_PATTERN (c, xs))
                    =>
                    decon::to_string c + listify("(", ", ", ")")  (map to_string xs);

                to_string (TUPLEPAT patterns)
                    =>
                    listify("(", ", ", ")") (map to_string patterns);

                to_string (RECORD_PATTERN lps)
                    =>
                    listify
                        ("{ ", ", ", " }") 
                        (map (\\ (l, p) =  var::to_string l + "=" + to_string p)
                             lps
                        );

                to_string (OR_PATTERN ps) => listify("(", " | ", ")") (map to_string' ps);
                to_string (ANDPAT ps) => listify("(", " and ", ")") (map to_string' ps);
                to_string (NOTPAT p)  => "not " + to_string' p;
                to_string (WHEREPAT (p, _, g)) => to_string p + " where " + gua::to_string g;

                to_string (NESTEDPAT (p, _, _, (_, e), p'))
                    =>
                    to_string p + " where " + exp::to_string e + " in " + to_string p'; 

                to_string (CONTPAT (v, p)) => to_string p  + " exception " +  var::to_string v;
            end 

            also
            fun to_string'(subst, p)
                =
                to_string p;

        };

        Rule_Number = Int;

        Dfa = DFA { stamp:      Int,                    # Unique dfa stamp 
                    free_vars:  Ref( name::set::Set ),  # Free variables 
                    ref_count:  Ref( Int ),             # Reference count 
                    generated:  Ref( Bool ),            # Has code been generated? 
                    height:     Ref( Int ),             # Dag height 
                    test:       Test                    # Type of tests 
                  }

        also
        Test
            = CASE    (Path, List ((Decon, List( Path ), Dfa)), Null_Or( Dfa ))         #  multiway 
            | WHERE   (gua::Guard, Dfa, Dfa)                                            # if test 
            | OK      (Rule_Number, act::Action)                                                # final dfa 
            | BIND    (Subst, Dfa)                                                      # Apply subst 
            | LET     (Path, ((Int, exp::Expression)), Dfa)                     # let 
            | SELECT  (Path, List ((Path, Index)), Dfa)                                 # projections 
            | CONT    (var::Var, Dfa)                                           # Bind fate 
            | FAIL                                                                      # error dfa 

        also
        Compiled_Dfa
            = 
            ROOT  { dfa:         Dfa, 
                    used:        name::set::Set,
                    exhaustive:  Bool,
                    redundant:   int_list_set::Set
                   }

        also
        Matrix
            = 
            MATRIX 
            { rows:   List( Row ),
              paths:  List( Path )                       #  path (per column) 
            }


        withtype Row =  
                   { patterns:  List( Pattern ), 
                    guard:     Null_Or( (Subst, gua::Guard) ),
                    nested:     List( (Subst, Path, ((Int, exp::Expression)), Pattern)),
                    dfa:       Dfa
                   } 
            also Compiled_Rule = 
                  (Rule_Number, List( Pattern ), Null_Or( gua::Guard ), Subst, act::Action)

            also Compiled_Pat = (Pattern, Subst);

        #  Utilities for dfas 
        #
        package dfa {

            itow = unt::from_int; 

            fun h (DFA { stamp, ... } )
                =
                itow stamp;

            fun hash (DFA { stamp, test, ... } )
                = 
                case test
                    #
                    FAIL    => 0u0;
                    OK _    => 0u123 + itow stamp;

                    CASE (path, cases, default)
                        =>
                        0u1234
                        +
                        fold_backward
                              (\\ ((_, _, x), y) =  h x + y) 
                              case default    THE x => h x;  NULL => 0u0; esac
                              cases;

                    SELECT(_, _, dfa) => 0u2313 + hash dfa;
                    CONT(_, dfa) => 0u1234 + hash dfa;
                    WHERE (g, yes, no) => 0u2343 + h yes + h no;
                    BIND(_, dfa) => 0u23234 + h dfa;
                    LET(_, (i, _), dfa) => itow i + h dfa + 0u843;
               esac;

            # Pointer equality:
            #
            fun eq (DFA { stamp=>s1, ... }, DFA { stamp=>s2, ... } )
                =
                s1 == s2;

            fun eq_opt (NULL, NULL) => TRUE;
                eq_opt (THE x, THE y) => eq (x, y);
                eq_opt _ => FALSE;
            end;

            # One-level equality:
            #
            fun equal ( DFA { test=>t1, stamp=>s1, ... },
                        DFA { test=>t2, stamp=>s2, ... }
                      )
                =
                case (t1, t2)
                    #
                    (FAIL, FAIL) => TRUE;

                    (OK _, OK _) => s1 == s2;

                    (SELECT (p1, b1, x), SELECT (p2, b2, y))
                        => 
                        path::equal (p1, p2)
                        and
                        eq (x, y)
                        and
                        forall
                            (\\ ((px, ix), (py, iy)) =  path::equal (px, py) and index::equal (ix, iy))
                            (b1, b2);

                    (CONT (k1, x), CONT (k2, y))
                        => 
                        var::compare (k1, k2) == EQUAL and eq (x, y);

                    (CASE (p1, c1, o1), CASE (p2, c2, o2))
                        =>
                         path::equal (p1, p2)
                         and 
                         forall
                            (\\ ((u, _, x), (v, _, y))
                                = 
                                decon::equal (u, v) and eq (x, y)
                            ) 
                            (c1, c2)
                         and
                         eq_opt (o1, o2);

                    ( WHERE (g1, y1, n1), 
                      WHERE (g2, y2, n2)
                    )
                        =>
                        gua::compare (g1, g2) == EQUAL 
                        and eq (y1, y2) and eq (n1, n2); 

                    ( BIND (s1, x),
                      BIND (s2, y)
                    )
                        =>
                        eq (x, y)
                        and
                        forall
                            (\\ ((p, x), (q, y))
                                 =
                                 var::compare (p, q) == EQUAL
                                 and 
                                 name::equal (x, y)
                            )
                            ( subst::keyvals_list s1,
                              subst::keyvals_list s2
                            );

                    (LET (p1, (i1, _), x), LET (p2, (i2, _), y))
                        =>
                        path::equal (p1, p2) and i1==i2 and eq (x, y);

                   _ => FALSE;
               esac;

                                                                         # typelocked_hashtable_g       is from   src/lib/src/typelocked-hashtable-g.pkg
            package hashtable
                = 
                typelocked_hashtable_g (
                    Hash_Key = Dfa;
                    same_key = equal;
                    hash_value = hash;
                );

            fun to_string (ROOT { dfa, ... } )
                =
                {   exception NOT_VISITED;

                    visited =   iht::make_hashtable  { size_hint => 32,  not_found_exception => NOT_VISITED };

                    fun mark stamp
                        =
                        iht::set visited (stamp, TRUE);

                    fun is_visited stamp
                        = 
                        null_or::the_else (iht::find visited stamp, FALSE);

 #                 include package   spp;

                    ++ = spp::CONS;

                    infix my ++ ;


                    fun pr_args []
                            =>
                            spp::NOP;

                        pr_args ps
                            =>
                            spp::LIST
                                { leftbracket  =>  spp::PUNCTUATION "(",
                                  separator    =>  spp::PUNCTUATION ", ",
                                  rightbracket =>  spp::PUNCTUATION ")",
                                  elements     =>  map (spp::ALPHABETIC o path::to_string) ps
                                };
                    end;

                    fun walk (DFA { stamp, test=>FAIL, ... } )
                            =>
                            spp::ALPHABETIC "fail";

                        walk (DFA { stamp, test, ref_count=>REF n, ... } ) =>

                        if (is_visited stamp)
                            #
                            spp::ALPHABETIC "goto" ++ spp::INT stamp; 
                        else
                            mark stamp;

                            spp::PUNCTUATION "<" ++ spp::INT stamp ++ spp::PUNCTUATION ">"
                            ++
                            if (n > 1)   spp::PUNCTUATION "*";
                            else         spp::NOP;
                            fi
                            ++
                            case test
                                #
                                OK(_, a) => spp::ALPHABETIC "Ok" ++ spp::ALPHABETIC (act::to_string a);

                                FAIL => spp::ALPHABETIC "FAIL";

                                SELECT (root, namings, body)
                                    => 
                                    spp::INDENTED_LINE (spp::ALPHABETIC "Stipulate")
                                    ++
                                    spp::INDENTED_BLOCK
                                        (spp::LIST
                                          { leftbracket  =>  spp::NOP,
                                            separator    =>  spp::NEWLINE,
                                            rightbracket =>  spp::NOP,
                                            elements     => (map (\\ (p, i)
                                                                     =
                                                                     spp::INDENT ++
                                                                     spp::ALPHABETIC (path::to_string p)    ++ spp::ALPHABETIC "=" ++ 
                                                                     spp::ALPHABETIC (path::to_string root) ++ spp::ALPHABETIC "." ++ 
                                                                     spp::ALPHABETIC (index::to_string i)
                                                                 )
                                                                 namings
                                                            )
                                          } 
                                        )
                                    ++
                                    spp::INDENTED_LINE (spp::ALPHABETIC "in")
                                    ++
                                    spp::INDENTED_BLOCK (walk body);

                                CONT (k, x)
                                    =>
                                    spp::INDENTED_LINE (spp::ALPHABETIC "Cont" ++ spp::ALPHABETIC (var::to_string k) ++ walk x);

                                CASE (p, cases, default)
                                    =>
                                    spp::INDENTED_LINE (spp::ALPHABETIC "Case" ++ spp::PUNCTUATION (path::to_string p))
                                    ++
                                    spp::INDENTED_BLOCK
                                        (spp::LIST
                                          { leftbracket  =>  spp::NOP,
                                            separator    =>  spp::NEWLINE,
                                            rightbracket =>  spp::NOP,
                                            elements     =>  (   (map (\\ (decon, args, dfa)
                                                                          =
                                                                          spp::INDENT ++ spp::ALPHABETIC (decon::to_string decon) ++ pr_args args
                                                                          ++ spp::ALPHABETIC "=>" ++ spp::MAYBE_BLANK ++ walk dfa
                                                                       )
                                                                       cases
                                                                 )

                                                                 @

                                                                 case default
                                                                     #
                                                                     NULL    =>  [];
                                                                     THE dfa =>  [spp::ALPHABETIC "_" ++ spp::ALPHABETIC "=>" ++ spp::MAYBE_BLANK ++ walk dfa];
                                                                 esac
                                                             )
                                          } 
                                     );

                                WHERE (g, y, n)
                                    =>
                                    spp::INDENTED_LINE (spp::ALPHABETIC "If" ++ spp::ALPHABETIC (gua::to_string g))
                                    ++
                                    spp::INDENTED_BLOCK (   spp::INDENT ++ spp::ALPHABETIC "then" ++ walk y ++ spp::NEWLINE
                                                            ++
                                                            spp::INDENT ++ spp::ALPHABETIC "else" ++ walk n
                                                        );

                                BIND (subst, x)
                                    =>
                                    spp::INDENTED_LINE (subst::keyed_fold_backward
                                             (\\ (v, n, prettyprint)
                                                 =
                                                 spp::INDENT ++ spp::ALPHABETIC (var::to_string v) ++ spp::PUNCTUATION "<-"
                                                 ++
                                                 spp::ALPHABETIC (name::to_string n) ++ prettyprint
                                             )
                                             spp::NOP
                                             subst
                                         ) ++
                                         walk x;

                                LET (path, ( _, e), x)
                                    =>
                                    spp::INDENTED_LINE (   spp::ALPHABETIC "Stipulate"
                                                       ++  spp::ALPHABETIC (path::to_string path)
                                                       ++  spp::ALPHABETIC "="
                                                       ++  spp::ALPHABETIC (exp::to_string e)
                                                       )
                                    ++
                                    spp::INDENTED_BLOCK (walk x);
                            esac; 
                        fi;
                    end;                                # fun walk

                    spp::prettyprint_expression_to_string (walk dfa ++ spp::NEWLINE);
                };
        };

        #  Utilities for the pattern matrix 
        #
        package matrix {

            fun row (MATRIX { rows, ... }, i)
                =
                list::nth (rows, i);

            fun col (MATRIX { rows, ... }, i)
                = 
                list::map
                    (\\ { patterns, ... } =  list::nth (patterns, i))
                    rows;

            fun path_of (MATRIX { paths, ... }, i)
                =
                list::nth (paths, i);

            fun column_count  m
                =
                list::length ((row (m, 0)).patterns);

            fun is_empty (MATRIX { rows => [], ... } )
                    =>
                    TRUE;

                is_empty _
                    =>
                    FALSE;
            end;

            fun remove_first_row (MATRIX { rows=>_ ! rows, paths } )
                    => 
                    MATRIX { rows, paths };

                remove_first_row _
                    =>
                    error "removeFirstRow";
            end;

            fun check (MATRIX { rows, paths, ... } )
                =
                {   arity = length paths;

                    apply
                        (\\ { patterns, ... }
                            =
                            if   (length patterns  !=  arity)   bug "bad matrix";   fi)
                        rows;
                };

            fun to_string (MATRIX { rows, paths, ... } )
                =
                listify
                    ("", "\n", "\n")
                    (map
                        (\\ { patterns, ... }
                            =
                            listify
                                ("[", "\t", "]")
                                (map pattern::to_string patterns))
                        rows
                    );


            # Given a matrix, find the best column for matching.
            #
            # I'm using the heuristic that John (Reppy) uses:
            # the first column i where pat_i0 is not a wild card, and
            # with the maximum number of distinct constructors in the
            # the column. 
            #
            # If the first row is all wild card, then return NULL.

            fun find_best_match_column (m as MATRIX { rows, ... } )
                = 
                {   if  sanity_check    check m;  fi;

                    if  debug      print (to_string m);  fi;

                    n_col =  column_count m;

                    fun score i         # Score of doing pattern matching on column i 
                        =
                        {   patterns_i  =  col (m, i);
                            patterns_i0 =  head patterns_i; 

                            case patterns_i0
                                #
                                WILDCARD_PATTERN =>   0;

                                _   =>
                                    {   my (cons, score)
                                            =
                                            # Count distinct constructors; skip refutable cards. 
                                            # Give records, tuples and or patterns, high scores
                                            # so that they are immediately expanded

                                            list::fold_backward
                                                \\ (WILDCARD_PATTERN, (sss, n))
                                                        =>
                                                        (sss, n);

                                                    (APPLY_PATTERN (c, _), (sss, n))
                                                        => 
                                                        (decon::set::add (sss, c), n);

                                                    (_, (sss, n))
                                                        =>
                                                        (sss, 10000);
                                                end
                                                (decon::set::empty, 0)
                                                patterns_i;

                                        score + decon::set::vals_count cons;
                                    };
                            esac;
                        };

                    # Find column with the highest score:
                    #
                    fun find_best (i, best_so_far)
                        =
                        if (i >= n_col)
                            #   
                            best_so_far;
                        else 
                            score_i = score i;

                            best =  if case best_so_far
                                            NULL                =>  TRUE;
                                            THE (_, best_score) =>  score_i > best_score;
                                       esac

                                        THE (i, score_i);
                                    else
                                        best_so_far;
                                    fi;

                            find_best (i+1, best);
                        fi;

                    case (find_best (0, NULL))
                        #
                        THE (i, 0) =>  NULL;   #  A score of zero means all wildcards 
                        THE (i, _) =>  THE i;
                        NULL       =>  NULL;
                    esac; 
                };                                      # fun find_best_match_column
        };              # package matrix 

        to_string =  dfa::to_string;


        # Rename user pattern into internal pattern.
        # The path business is hidden from the client.
        #
        fun rename do_it
              { number => rule_no,
                patterns,
                guard,
                action,
                match_fail_exception                    # Currently ignored. I think intended to allow end-user selection of exception generated on match failure. -- 2011-04-23 CrT
              }
            =
            {   empty =  subst::empty;

                fun bind (subst, v, p)
                    =
                    case (subst::get (subst, v))
                        #
                        NULL  =>  subst::set (subst, v, PVAR p);
                        THE _ =>  error("duplicated pattern variable " + var::to_string v);
                    esac;

                fun process (path, subst: Subst, pattern) : Compiled_Pat
                    = 
                    {   fun id_pattern id
                            =
                            (WILDCARD_PATTERN, bind (subst, id, path));

                        fun as_pattern (id, p)
                            = 
                            {   my (p, subst)
                                    =
                                    process (path, subst, p);

                                (p, bind (subst, id, path));
                            };

                        fun wild_pattern ()
                            =
                            (WILDCARD_PATTERN, subst);

                        fun lit_pattern  lit
                            =
                            (APPLY_PATTERN (LIT lit, []), subst);

                        fun process_patterns  patterns
                            = 
                            loop (patterns, 0, [], subst)
                            where
                                fun loop ([], _, ps', subst)
                                        =>
                                        (reverse ps', subst);

                                    loop (p ! ps, i, ps', subst)
                                        => 
                                        {   path' =  path::dot (path, INT i);

                                            my (p, subst)
                                                =
                                                process (path', subst, p);

                                            loop (ps, i+1, p ! ps', subst);
                                        };
                                end;
                            end;

                        fun process_lpatterns (lpatterns)
                            = 
                            loop (lpatterns, [], subst)
                            where
                                fun loop ([], ps', subst)
                                        =>
                                        (reverse ps', subst);

                                    loop((l, p) ! ps, ps', subst)
                                        => 
                                        {   path' =  path::dot (path, LABEL l);

                                            my (p, subst)
                                                =
                                                process (path', subst, p);

                                            loop (ps, (l, p) ! ps', subst);
                                        };
                                end;
                            end;

                        fun cons_pattern (c, args): Compiled_Pat
                            = 
                            {   my (patterns, subst)
                                    =
                                    process_patterns (args);

                                # Arity check:
                                #
                                if (con::arity c != length args )
                                    #
                                    error ("arity mismatch " + con::to_string c);
                                fi;

                                (APPLY_PATTERN (CON c, patterns), subst); 
                            };

                        fun tuple_pattern (patterns): Compiled_Pat
                            = 
                            {   my (patterns, subst) = process_patterns (patterns);
                                #
                                (TUPLEPAT patterns, subst);
                            };

                        fun record_pattern (lpatterns): Compiled_Pat
                            = 
                            {   my (lpatterns, subst) = process_lpatterns (lpatterns);
                                #
                                (RECORD_PATTERN lpatterns, subst);
                            };

                        fun no_dupl (subst, subst')
                            =
                            {   duplicated
                                    =
                                    var_set::vals_list (
                                        #
                                        var_set::intersection (
                                            #
                                            var_set::add_list (var_set::empty, subst::keys_list subst'),
                                            var_set::add_list (var_set::empty, subst::keys_list subst )
                                        )
                                    );

                                case duplicated
                                    #
                                    [] =>  ();
                                    _  =>  error ("duplicated pattern variables: " + listify("", ", ", "") (map var::to_string duplicated));
                                esac;
                            };

                        # Or patterns are tricky because the same variable name
                        # may be bound to different components.  We handle this by renaming
                        # all variables to some canonical set of paths, 
                        # then rename all variables to these paths. 
                        #
                        fun logical_pattern (name, name2, f)  []
                                =>
                                error("empty " + name + " pattern");

                            logical_pattern (name, name2, f)  patterns
                                => 
                                {   results   =  map (\\ p => process (path, empty, p); end ) patterns;
                                    ps        =  map #1 results;
                                    or_substs =  map #2 results;


                                    fun same_vars ([], s')
                                            =>
                                            TRUE;

                                        same_vars (s ! ss, s')
                                            => 
                                            forall
                                                (\\ (x, y) =  var::compare (x, y) == EQUAL) 
                                                (subst::keys_list s, s')
                                            and
                                            same_vars (ss, s');
                                    end;


                                    # Make sure all patterns use
                                    # the same set of variable names:

                                    or_names = subst::keys_list (head or_substs);

                                    if (not (same_vars (tail or_substs, or_names)))

                                        error("not all " + name2 + " have the same variable namings");
                                    fi;

                                    no_dupl (subst, head or_substs);

                                    # Build the new substitution to
                                    # include all names in the or   
                                    # patterns.

                                    subst = subst::keyed_fold_backward  
                                                 (\\ (v, _, subst) =  subst::set (subst, v, VAR v))
                                                 subst
                                                 (head or_substs); 

                                  (f (paired_lists::zip (or_substs, ps)), subst);
                                };
                        end;

                        fun or_pattern  patterns = logical_pattern ("or", "disjuncts", OR_PATTERN) patterns;
                        fun and_pattern patterns = logical_pattern ("and", "conjuncts", ANDPAT) patterns;

                        fun not_pattern pattern
                            = 
                            {   my (pattern, subst')  = process (path, empty, pattern);
                                no_dupl (subst, subst');
                                (NOTPAT (subst', pattern), subst);
                            };

                        fun where_pattern (pattern, e)
                            =
                            {   my (pattern, subst') = process (path, empty, pattern);
                                no_dupl (subst, subst');
                                (WHEREPAT (pattern, subst', e), subst);
                            };

                        fun nested_pattern (pattern1, e, pattern2)
                            =
                            {   path' = path::dot (path, INT -1);

                                my (pattern1, subst1) = process (path, subst, pattern1);
                                my (pattern2, subst2) = process (path', subst1, pattern2);

                                (NESTEDPAT (pattern1, subst1, path', e, pattern2), subst2);
                            }; 

                      do_it { id_pattern,
                              as_pattern,
                              wild_pattern,
                              cons_pattern,
                              tuple_pattern,
                              record_pattern,
                              lit_pattern,
                              or_pattern,
                              and_pattern,
                              not_pattern,
                              where_pattern,
                              nested_pattern
                             } pattern;
                    };                          # fun process


                fun process_all_patterns (i, [], subst, ps')
                        =>
                        (reverse ps', subst);

                    process_all_patterns (i, p ! ps, subst, ps')
                        =>
                        {   my (p, subst) = process (PATH [INT i], subst, p);
                            process_all_patterns (i+1, ps, subst, p ! ps');
                        };
                end;

                (process_all_patterns (0, patterns, empty, []))
                    ->
                    (patterns, subst);
                    

                (rule_no, patterns, guard, subst, action);
            };

        package dfamap
           = 
           red_black_map_g (                                            # red_black_map_g               is from   src/lib/src/red-black-map-g.pkg
               Key = Dfa; 
               fun st (DFA { stamp, ... } ) = stamp;
               fun compare (x, y) = int::compare (st x, st y);
            );


        # Give the arguments to case,
        # factor out the common case
        # and make it  the default.
        #
        fun factor_case (p, cases, d as THE _)
                =>
                (p, cases, d);

            factor_case (p, cases, NULL)
                => 
                {   fun count (m, dfa)
                        =
                        the_else (dfamap::get (m, dfa), 0);

                    fun inc ((_, _, dfa), m)
                        =
                        dfamap::set (m, dfa, 1 + count (m, dfa));

                     m =  fold_backward  inc  dfamap::empty  cases;

                     best
                         =
                         dfamap::keyed_fold_backward 

                             \\ (dfa, c, NULL)
                                    =>
                                    THE (dfa, c);

                                (dfa, c, best as THE(_, c'))
                                    =>
                                    if (c > c')   THE (dfa, c);
                                    else          best;
                                    fi;
                             end

                             NULL
                             m;  

                     fun neq (DFA { stamp=>x, ... }, DFA { stamp=>y, ... } )
                         =
                         x != y;

                     case best

                          NULL       =>  (p, cases, NULL); 
                          THE (_, 1) =>  (p, cases, NULL); 

                          THE (default_case, n)
                              => 
                              {   others
                                      =
                                      list::filter
                                          (\\ (_, _, x) =  neq (x, default_case))
                                          cases;

                                  (p, others, THE default_case); 
                              };
                     esac;
                };
        end;                    # fun factor_case



        # The main pattern matching compiler.
        # The dfa states are constructed with hash consing at the same time
        # so no separate DFA minimization step is needed.
        #
        fun compile { compiled_rules, compress }
            =
            {   exception NO_SUCH_STATE;

                Expand_Type
                    = SWITCH  (List ((Decon, List( Path ), Matrix)), Null_Or( Matrix ))
                    | PROJECT  (Path,  List ((Path, Index)), Matrix);

                fun simp x
                    =
                    if compress      factor_case x;
                    else             x;
                    fi;

                # Table for hash consing:
                #
                dfa_table = dfa::hashtable::make_hashtable  { size_hint => 32,  not_found_exception => NO_SUCH_STATE }
                          : dfa::hashtable::Hashtable( Dfa );

                lookup_state
                    =
                    dfa::hashtable::get  dfa_table;

                insert_state
                    =
                    dfa::hashtable::set  dfa_table;

                stamp_counter =  REF 0;

                fun mk_state (test)
                    =   
                    {   stamp = *stamp_counter;
                        stamp_counter := stamp + 1;

                        DFA { stamp, free_vars=>REF name::set::empty, 
                              height=>REF 0, ref_count=>REF 0, generated=>REF FALSE, test
                            };
                    };

                fun new_state test
                    =
                    {   s = mk_state (test);

                        lookup_state s
                        except
                            NO_SUCH_STATE = {  insert_state (s, s);
                                               s;
                                            };
                    };


                # State constructors 

                fail =  new_state (FAIL);

                fun ok x
                    =
                    new_state (OK x);

                fun case'(_, [], THE x) =>   x;
                    case'(_, [], NULL)  =>   fail;

                    case' (p, cases as (_, _, c) ! cs, default)
                        => 
                        if ( list::all
                                 (\\ (_, _, c') =  dfa::eq (c, c'))
                                 cs

                             and

                            case default
                                #
                                THE x =>  dfa::eq (c, x);     
                                NULL  =>  TRUE;
                            esac
                        )
                            c;
                        else
                            new_state (CASE (simp (p, cases, default)));
                        fi;
                end;

                fun select (x) =  new_state  (SELECT (x));
                fun cont   (x) =  new_state  (CONT   (x));

                fun where' (g, yes, no)
                    = 
                    if   (dfa::eq (yes, no))

                         yes;
                    else
                         new_state (WHERE (g, yes, no));
                    fi;

                fun bind (subst, x)
                    =
                    subst::vals_count subst == 0
                      ??  x
                      ::  new_state (BIND (subst, x));


                fun let' x
                    =
                    new_state (LET x);


                # Expand column i, 
                # Return a new list of matrixes indexed by the deconstructors.

                fun expand_column (m as MATRIX { rows, paths, ... }, i)
                    = 
                    {   ith_col =  matrix::col     (m, i);
                        path_i  =  matrix::path_of (m, i);

                        if debug
                             print ("Expanding column " +  i2s i  + "\n");
                        fi;

                        fun split_i ps
                            =
                            loop (0, ps, [])
                            where
                                fun loop (j, p ! ps, ps')
                                        =>
                                        if (i == j)
                                            #
                                            (reverse ps', p, ps); 
                                        else
                                            loop (j+1,  ps,  p ! ps');
                                        fi;

                                    loop _
                                        =>
                                        bug "split_i";
                                end;
                            end;

                        # If the ith column cfind out what to expand 
                        #
                        fun expand ((p as OR_PATTERN _) ! ps, this) =>  THE p;
                            expand ((p as ANDPAT _) !     ps, this) =>  THE p;
                            expand ((p as NOTPAT _) !     ps, this) =>  THE p;
                            expand ((p as WHEREPAT _) !   ps, this) =>  THE p;
                            expand ((p as NESTEDPAT _) !  ps, this) =>  THE p;
                            expand ((p as CONTPAT _) !    ps, this) =>  THE p;

                            expand ((p as TUPLEPAT _) !       ps, this) =>  expand (ps, THE p);
                            expand ((p as RECORD_PATTERN _) ! ps, this) =>  expand (ps, THE p);
                            expand ((p as APPLY_PATTERN _) !  ps, this) =>  expand (ps, THE p);
                            expand (WILDCARD_PATTERN !        ps, this) =>  expand (ps, this);

                            expand([], this) =>  this;
                        end;

                        # Split the paths:
                        # 
                        my (prev_paths, _, next_paths)
                            =
                            split_i paths;

                        case (expand (ith_col, NULL))
                            #
                            THE (NOTPAT _)                              # Expand not patterns.
                                =>
                                expand (rows, [])
                                where

                                    fun expand ([], _)
                                            =>
                                            bug "expand NOT"; 

                                        expand ((row as { patterns, guard, nested, dfa } ) ! rows, rows')
                                            => 
                                            {   my (prev, pat_i, next)
                                                    =
                                                    split_i  patterns;

                                                case pat_i

                                                     NOTPAT (subst, p)
                                                         =>
                                                         {   rows' = reverse rows';

                                                             yes   = { patterns => prev @ [WILDCARD_PATTERN] @ next,
                                                                       nested,
                                                                       guard,
                                                                       dfa
                                                                     };

                                                             m2 = MATRIX { rows, paths };

                                                             no = { patterns =>  prev @ [p] @ next,
                                                                    guard    =>  NULL, 
                                                                    nested   =>  [],
                                                                    dfa      =>  bind (subst, match m2)
                                                                  };

                                                             m1 = MATRIX { rows  => rows' @ [no, yes] @ rows,
                                                                           paths
                                                                         };

                                                             expand_column (m1, i);
                                                         };

                                                     _ => expand (rows, row ! rows');
                                                esac;
                                            };
                                    end;                        # fun expand
                                end;                            # THE (NOTPAT _)

                            THE (OR_PATTERN _ | WHEREPAT _ | NESTEDPAT _)
                                => 
                                # If we have or/where patterns then expand all rows
                                # with these patterns
                                #
                                {   fun expand (row as { patterns, dfa, nested, guard } )
                                        =
                                        {   my (prev, pat_i, next)
                                                =
                                                split_i  patterns;

                                            case pat_i
                                                #
                                                OR_PATTERN ps
                                                    =>
                                                    map
                                                        (\\ (subst, p)
                                                            =
                                                            { patterns =>  prev @ [p] @ next,
                                                              dfa      =>  bind (subst, dfa),
                                                              nested,
                                                              guard
                                                            }
                                                        )
                                                        ps;

                                                WHEREPAT (p, subst', g)
                                                    =>
                                                    [   { patterns =>  prev @ [p] @ next,
                                                          dfa,
                                                          nested,
                                                          guard    =>  case guard

                                                                            NULL
                                                                                =>
                                                                                THE (subst', g);

                                                                            THE (subst, g')
                                                                                => 
                                                                                THE ( merge_subst (subst, subst'),
                                                                                      gua::logical_and (g, g')
                                                                                    );
                                                                       esac
                                                        }
                                                    ];

                                                NESTEDPAT (pattern, subst, path, expression, pattern')
                                                    =>
                                                    [   { patterns =>  prev @ [pattern] @ next,
                                                          dfa,
                                                          nested     =>  (subst, path, expression, pattern') ! nested,
                                                          guard
                                                        }
                                                    ];

                                                _ =>  [row];
                                            esac;
                                        };                              # fun expand

                                    new_matrix
                                        =
                                        MATRIX { rows  => list::cat (map expand rows),
                                                 paths
                                               };

                                    expand_column (new_matrix, i);
                                };                                              # THE (OR_PATTERN _ | WHEREPAT _ | NESTEDPAT _)

                            THE (TUPLEPAT patterns)     #  expand a tuple along all the columns 
                                =>
                                {   arity =  length patterns;

                                    wilds =  map
                                                 (\\ _ =  WILDCARD_PATTERN)
                                                 patterns;

                                    fun process_row { patterns, nested, dfa, guard }
                                        =
                                        {   my (prev, pat_i, next)
                                                =
                                                split_i  patterns;

                                            case pat_i
                                                #
                                                TUPLEPAT ps'
                                                    =>
                                                    {   n =  length ps';

                                                        if   (n != arity)

                                                             error ("tuple arity mismatch");
                                                        fi;

                                                        { patterns => prev @ ps' @ next,
                                                          nested,
                                                          dfa,
                                                          guard
                                                        };
                                                    };

                                                WILDCARD_PATTERN
                                                    => 
                                                    { patterns=>prev @ wilds @ next,
                                                      nested,
                                                      dfa,
                                                      guard
                                                    };

                                                pattern
                                                    =>
                                                    error ("mixing tuple and: " + pattern::to_string pattern);
                                            esac;
                                        };

                                    rows    =  map process_row rows;

                                    path_i' =  list::from_fn (
                                                 arity,
                                                 \\ i =  path::dot (path_i, INT i)
                                               );

                                    paths   =  prev_paths @ path_i' @ next_paths;

                                    namings =  list::from_fn (
                                                 arity,
                                                 \\ i =  (path::dot (path_i, INT i), INT i)
                                               );

                                    PROJECT (
                                        path_i,
                                        namings,
                                        MATRIX { rows, paths }
                                    );
                                };                                      # THE (TUPLEPAT patterns)


                            THE (RECORD_PATTERN _)    #  expand a tuple along all the columns 
                                =>
                                {   # All the labels that are in this column:
                                    #
                                    labels = 
                                        var_set::vals_list (
                                            #
                                            list::fold_backward
                                                #
                                                \\ (RECORD_PATTERN lps, lll)
                                                       => 
                                                       list::fold_backward
                                                           (\\ ((l, p), lll) =  var_set::add (lll, l))
                                                           lll
                                                           lps;
                                                   (_, lll)
                                                       =>
                                                       lll;
                                                end

                                                var_set::empty

                                                ith_col
                                        );

                                    if debug
                                         print("Labels=" + listify("", ", ", "") 
                                                        (map var::to_string labels) + "\n");
                                    fi;

                                    fun lp2s (l, p)
                                        =
                                        var::to_string l + "=" + pattern::to_string p;

                                    fun lps2s lps
                                        =
                                        listify ("", "\t", "") (map lp2s lps);

                                    fun ps2s ps
                                        =
                                        listify ("", "\t", "") (map pattern::to_string ps);

                                    wilds
                                        =
                                        map
                                            (\\ _ =  WILDCARD_PATTERN)
                                            labels;

                                    fun process_row { patterns, nested, dfa, guard }
                                        =
                                        {   my (prev, pat_i, next)
                                                =
                                                split_i (patterns);

                                            case pat_i
                                                #
                                                RECORD_PATTERN lps
                                                    =>
                                                    #  Put lps in canonical order 
                                                    {   lps = pattern::sort_by_label lps;

                                                        debug   ?:   print ("lpatterns=" + lps2s lps + "\n");

                                                        fun collect ([], [], ps')
                                                                =>
                                                                reverse ps';

                                                            collect (x ! xs, [], ps')
                                                                => 
                                                                collect (xs, [], WILDCARD_PATTERN ! ps');

                                                            collect (x ! xs, this as (l, p) ! lps, ps')
                                                                =>
                                                                case (var::compare (x, l))

                                                                     EQUAL   =>  collect (xs, lps, p ! ps');
                                                                     LESS    =>  collect (xs, this, WILDCARD_PATTERN ! ps');
                                                                     GREATER =>  error "labels out of order";
                                                                esac;

                                                            collect _
                                                                =>
                                                                bug "processRow";
                                                        end;

                                                        ps = collect (labels, lps, []);

                                                        debug   ?:   print("new patterns=" + ps2s ps + "\n");

                                                        { patterns =>  prev @ ps @ next,
                                                          nested,
                                                          dfa,
                                                          guard
                                                        };
                                                    };                                  # RECORD_PATTERN lps


                                                WILDCARD_PATTERN
                                                    => 
                                                    { patterns =>  prev @ wilds @ next,
                                                      nested,
                                                      dfa,
                                                      guard
                                                    };

                                                pattern
                                                    =>
                                                    error ("mixing record and: " + pattern::to_string pattern);
                                            esac;
                                        };                              # fun process_row 


                                    rows  =  map  process_row  rows;

                                    path_i'
                                        =
                                        map
                                            (\\ l =  path::dot (path_i, LABEL l))
                                            labels;

                                    paths =  prev_paths
                                          @  path_i'
                                          @  next_paths;

                                    namings
                                        =
                                        map
                                            (\\ l =  (path::dot (path_i, LABEL l), LABEL l))
                                            labels;

                                    PROJECT (
                                        path_i,
                                        namings,
                                        MATRIX { rows, paths }
                                    );
                                };

                            THE (APPLY_PATTERN (decon, _))
                                => 
                                # Find out how many variants
                                # there are in this case:
                                #
                                {   fun get_variants ()
                                        = 
                                        decon::set::vals_list 
                                          (list::fold_backward 
                                              \\ (APPLY_PATTERN (x, _), sss) => decon::set::add (sss, x);
                                                  (_, sss)                   => sss;
                                              end
                                              decon::set::empty
                                              ith_col
                                          );

                                    my (all_variants, has_default)
                                        =
                                        case decon
                                            #
                                            CON c => 
                                                {   (con::variants c) ->   { known, others };

                                                    ( case known
                                                           [] => get_variants(); 
                                                           _  => map CON known;
                                                      esac,

                                                      others
                                                    ); 
                                                };

                                            LIT l
                                                => 
                                                case (lit::variants l)
                                                    #
                                                    THE { known, others } =>  (map LIT known, others);
                                                    NULL                  =>  (get_variants(), TRUE);
                                                esac;
                                        esac; 

                                    # function from con -> matrix; initially no rows 
                                    #
                                    fun insert (table, key, x)
                                        =
                                        decon::map::set (table, key, x);

                                    fun lookup (table, key)
                                        = 
                                        case (decon::map::get (table, key))
                                            #
                                            THE x => x;
                                            NULL  => bug("can't find constructor " + decon::to_string key);
                                        esac;


                                    empty = decon::map::empty;


                                    fun create ([], table)
                                            =>
                                            table;

                                        create((con as CON c) ! cons, table)
                                            =>
                                            {   n     =  con::arity c;
                                                paths =  list::from_fn
                                                      (n, \\ i = path::dot (path_i, INT i));
                                                create (cons, insert (table, con, { args => paths, rows => [] } ));
                                            };

                                        create((con as LIT l) ! cons, table)
                                            =>
                                            create (cons, insert (table, con, { args => [], rows => [] } ));
                                    end;


                                    table =   create (all_variants, empty);


                                    fun insert_row (table, decon, row)
                                        =
                                        {   my { args, rows } = lookup (table, decon);
                                            insert (table, decon, { args, rows => rows @ [row] } );
                                        };


                                    fun foreach_row ([], table)
                                            =>
                                            table;

                                        foreach_row( { patterns, dfa, nested, guard } ! rows, table)
                                            =>
                                            {   (split_i patterns) ->   (prev, pat_i, next);

                                                fun add_row (table, decon, patterns)
                                                    = 
                                                    insert_row
                                                      (
                                                        table,
                                                        decon, 
                                                        { patterns, nested, dfa, guard }
                                                      );

                                                fun add_wild_to_every_row (table)
                                                    =
                                                    fold_backward

                                                        (\\ (c, table)
                                                            =
                                                            {   my { args, rows } = lookup (table, c);
                                                                wilds = map (\\ _ => WILDCARD_PATTERN; end ) args;
                                                                patterns  = prev @ wilds @ next;
                                                                add_row (table, c, patterns);
                                                            }
                                                        )

                                                        table

                                                        all_variants;

                                                table = case pat_i
                                                            #
                                                            WILDCARD_PATTERN
                                                                =>
                                                                add_wild_to_every_row table;

                                                            APPLY_PATTERN (decon, args)
                                                                =>
                                                                {   patterns = prev @ args @ next;
                                                                    add_row (table, decon, patterns);
                                                                };

                                                            _ => error "expecting constructor but found tuple/record";
                                                        esac;

                                                foreach_row (rows, table);
                                            };
                                    end;

                                    table =   foreach_row (rows, table);

                                    fun collect_cases (decon, { args, rows }, rules)
                                        = 
                                        {   matrix =  MATRIX { rows, paths=>prev_paths @ args @ next_paths };

                                            (decon, args, matrix) ! rules;
                                        };

                                    cases =   decon::map::keyed_fold_backward collect_cases [] table;

                                    # If we have a default then the default matrix
                                    # contains the original matrix with rows whose
                                    # column i is the wild card.
                                    #
                                    default
                                        =
                                        if (not has_default)
                                            #   
                                            NULL;
                                        else
                                            THE(
                                                MATRIX { rows=>list::filter 
                                                                   (\\ { patterns, ... }
                                                                      =
                                                                      case (list::nth (patterns, i))

                                                                           WILDCARD_PATTERN =>  TRUE;
                                                                           _                =>  FALSE;
                                                                      esac
                                                                   )
                                                                   rows,
                                                                   paths
                                                       }
                                           );   
                                        fi;

                                    SWITCH (decon::map::keyed_fold_backward collect_cases [] table, default);
                                };

                            THE p => bug ("expand_column: " + pattern::to_string p);
                            NULL  => bug "expand_column";
                        esac;
                    }                           # fun expand_column 


                # Generate the DFA

                also
                fun match matrix
                    =
                    if (matrix::is_empty matrix)
                        #
                        fail;
                    else
                        case (matrix::find_best_match_column matrix)
                            #
                            NULL =>
                                # First row is all wild cards.
                                #
                                case (matrix::row (matrix, 0))
                                    #
                                    { guard => THE (subst, g), nested => [], dfa, ... }
                                        => 
                                        # Generate guard:
                                        #
                                        bind (subst,
                                            where' (g, dfa, 
                                                  match (matrix::remove_first_row matrix)));

                                    { guard => NULL, dfa, nested => [], ... }
                                        =>
                                        dfa;

                                    { guard, patterns, nested=>n ! ns, dfa, ... }
                                        => 
                                        # Handle nested patterns:
                                        # 
                                        {   n      ->  (subst, path, expression, pattern);
                                            matrix ->  MATRIX { rows, paths };

                                            row0  = { guard, patterns=>pattern ! patterns,
                                                         nested=>ns, dfa };
                                            rows' = tail rows;

                                            rows' = map (\\ { patterns, nested, dfa, guard }
                                                            =
                                                            { patterns=>WILDCARD_PATTERN ! patterns, nested, dfa, guard }
                                                        )
                                                        rows';

                                            m = MATRIX { rows=>row0 ! rows', paths=>path ! paths };

                                           bind (subst, let' (path, expression, match m));
                                        };
                                esac;

                            THE i => 
                                # Mixture rule; split at column i
                                # 
                                case (expand_column (matrix, i))
                                    #
                                    # Splitting a constructor:
                                    # 
                                    SWITCH (cases, default)
                                        =>
                                        {   cases = map (\\ (c, p, m) = (c, p, match m))
                                                        cases;

                                            case' (matrix::path_of (matrix, i), cases, 
                                                 null_or::map match default);
                                        };

                                    # Splitting a tuple or record;
                                    # recompute new namings.
                                    #
                                    PROJECT (p, namings, m)
                                        =>
                                        select (p, namings, match m);
                                esac;
                        esac;
                   fi;

                fun make_matrix rules
                    =
                    {   (head rules) ->   (_, patterns0, _, _, _);

                        arity =  length patterns0;

                        fun make_row (r, patterns, NULL, subst, action)
                                =>
                                { patterns,
                                  guard  =>  NULL,
                                  nested =>  [],
                                  dfa    =>  bind (subst, ok (r, action))
                                };

                            make_row (r, patterns, THE g, subst, action)
                                => 
                                { patterns,
                                  guard  =>  THE (subst, g),
                                  nested =>  [],
                                  dfa    =>  ok (r, action)
                                };
                        end;

                        MATRIX {
                          rows  =>  map  make_row  rules,
                          paths =>  list::from_fn (arity,  \\ i =  PATH [INT i] )
                        };
                    };

                dfa =  match (make_matrix compiled_rules);

                rule_nos =  map #1 compiled_rules;


                # 1. Update the reference counts. 
                # 2. Compute the set of free path variables at each state. 
                # 3. Compute the set of path variables that are actually used.
                # 4. Compute the height of each node.

                exception NOT_VISITED;

                visited =   iht::make_hashtable  { size_hint => 32,  not_found_exception => NOT_VISITED };

                fun mark s
                    =
                    iht::set visited (s, TRUE);

                fun is_visited s
                    =
                    the_else (iht::find visited s, FALSE);

                fun set (fv, s)
                    =
                    {   fv := s;
                        s;
                    };

                fun set_h (height, h)
                    =
                    {   height := h;
                        h;
                    };

                union =  name::set::union;
                diff  =  name::set::difference;
                add   =  name::set::add;
                empty =  name::set::empty;

                fun diff_paths (fvs, ps)
                    = 
                    diff (fvs, name::set::add_list (name::set::empty, map PVAR ps));

                used = REF name::set::empty;

                fun occurs s
                    =
                    used := name::set::union (*used, s);

                redundant =   REF (int_list_set::add_list (int_list_set::empty, rule_nos));

                fun rule_used r
                    =
                    redundant :=  int_list_set::drop (*redundant, r);

                fun vars subst
                    =
                    name::set::add_list  (empty,  subst::vals_list subst);

                fun visit (DFA { stamp, ref_count, test, free_vars, height, ... }, pvs)
                    = 
                    {   ref_count :=  *ref_count + 1;
                        #
                        if (is_visited stamp)
                            #
                            (*free_vars, *height);
                        else
                            mark stamp;

                            case test
                                #
                                FAIL =>   (empty, 0);

                                BIND (subst, dfa)
                                    => 
                                    {   patvars =  name::set::add_list (empty, 

                                        map VAR (subst::keys_list subst));

                                        my (s, h)
                                            =
                                            visit (dfa, union (pvs, patvars));

                                        variables =  vars subst;
                                        s'        =  union (s, variables);
                                        s'        =  diff  (s', patvars); 

                                        occurs s'; 

                                        (set (free_vars, s'), set_h (height, h + 1));
                                    };

                                LET (p, _, dfa)
                                    =>
                                    {   (visit (dfa, pvs)) ->   (s, h);
                                        #
                                        (set (free_vars, s), set_h (height, h+1));
                                    };

                                OK (rule_no, action)
                                    => 
                                    {   fvs =  name::set::add_list (empty, 
                                                    map VAR (act::free_vars action));

                                        #   (print("Action = " + act::to_string action + "\n");
                                        #    print("PVs = " + Name::setToString PVs + "\n");
                                        #    print("FVs = " + Name::setToString fvs + "\n")
                                        #   )

                                        fvs = name::set::intersection (pvs, fvs);
                                        rule_used rule_no; 
                                        (set (free_vars, fvs), 0);
                                    };

                                CASE (p, cases, opt)
                                    =>
                                    {   my (fvs, h)
                                            = 
                                            list::fold_backward
                                                (\\ ((_, ps, x), (s, h))
                                                    =
                                                    {   my (fv, h')
                                                            =
                                                            visit (x, pvs);

                                                        fv =  diff_paths (fv, ps);

                                                        (union (fv, s), int::max (h, h'));
                                                    }
                                                )
                                                (empty, 0)
                                                cases; 

                                        my (fvs, h)
                                            =  
                                            case opt
                                                #
                                                NULL =>  (fvs, h); 

                                                THE x
                                                    => 
                                                    {   my (fv, h')
                                                            =
                                                            visit (x, pvs);

                                                        (union (fvs, fv), int::max (h, h'));
                                                    };
                                            esac;

                                        fvs =  add (fvs, PVAR p); 

                                        occurs fvs; 

                                        (set (free_vars, fvs), set_h (height, h+1));
                                    }; 

                                WHERE(_, y, n)
                                    => 
                                    {   my (sy, hy) =  visit (y, pvs);
                                        my (sn, hn) =  visit (n, pvs);

                                        s =  union (sy, sn);
                                        h =  int::max (hy, hn) + 1;

                                        occurs s; 

                                        (set (free_vars, s), set_h (height, h));
                                    };

                                SELECT (p, bs, x)
                                    => 
                                    {   my (s, h) =  visit (x, pvs);

                                        s   =  add (s, PVAR p);

                                        bs  =  fold_backward
                                                   (\\ ((p, _), sss) =  add (sss, PVAR p))
                                                   s
                                                   bs; 

                                        fvs =  diff (s, bs);
                                        occurs bs; 

                                        (set (free_vars, fvs), set_h (height, h+1)); 
                                    }; 

                                CONT (k, x)
                                    =>
                                    {   my (s, h) =  visit (x, pvs);    #  Always generate a state function 

                                        ref_count :=  *ref_count + 1; 

                                        (set (free_vars, s), set_h (height, h+1));
                                    };
                            esac; 
                       fi;
                    };

                visit (dfa, empty); 

                my DFA { ref_count=>fail_count, ... }
                    =
                    fail;

                ROOT {
                  used       =>   *used, 
                  dfa, 
                  exhaustive =>   *fail_count == 0, 
                  redundant  =>   *redundant
                };
            };

        fun exhaustive (ROOT { exhaustive, ... } ) =   exhaustive;
        fun redundant  (ROOT { redundant,  ... } ) =   redundant;


        # Generate final code for pattern matching.
        #
        fun code_gen 
             { gen_fail:  Void -> A_expression,
               gen_ok,   
               gen_path,   
               gen_bind,   
               gen_case,
               gen_if:    (gua::Guard, A_expression, A_expression) -> A_expression,
               gen_goto,
               gen_fun, 
               gen_let:   (List( A_decl ), A_expression) -> A_expression,
               gen_proj:  (Path,  List( (Null_Or( Path ), Index) )) -> A_decl,
               gen_variable:   Path -> var::Var,
               gen_val:   (var::Var, A_expression) -> A_decl,
               gen_cont 
             } (root, dfa)
        = 
        {   dfa ->   ROOT { dfa, used, ... };

            fun gen_pattern p
                =
                if (name::set::member (used, PVAR p))   THE p;
                else                                    NULL;
                fi; 

            #  fun arg p = THE p 

            fun make_vars free_var_set
                = 
                map (\\ PVAR p =>  gen_variable p;
                        VAR  v =>  v;
                     end 
                    )
                    (name::set::vals_list *free_var_set);

            fun enque (dfa, (fff, bbb))
                =
                (fff, dfa ! bbb);

            empty_queue =  ([], []);


            # Walk a state, if it is shared then
            # just generate a goto to the state
            # function; otherwise expand it: 
            #
            fun walk (dfa as DFA { stamp, ref_count, generated, free_vars, ... },
                                work_list)
                = 
                if (*ref_count > 1)
                    #
                    code =  gen_goto (stamp, make_vars free_vars);                   # Just generate a goto.

                    if *generated
                        #
                        (code, work_list);
                    else
                        generated := TRUE;

                        (code, enque (dfa, work_list));
                    fi;
                else
                    expand_dfa (dfa, work_list);
                fi 

            # Generate a new function definition:
            #
            also
            fun gen_new_fun (dfa as DFA { stamp, free_vars, height, ... }, work_list)
                =
                {   my (body, work_list)
                        =
                        expand_dfa (dfa, work_list);

                    ((*height, gen_fun (stamp, make_vars free_vars, body)), work_list); 
                }

            also
            fun expand_yes_no (yes, no, work_list)
                =
                (yes, no, work_list)
                where
                    my (yes, work_list) =  walk (yes, work_list);
                    my (no,  work_list) =  walk (no,  work_list);
                end

            # Expand the dfa always:
            #
            also
            fun expand_dfa (DFA { stamp, test, free_vars, ... }, work_list)
                =  
                case test
                    #
                    OK (rule_no, action)                #  Action 
                        =>
                        (gen_ok (action), work_list);

                    FAIL                                #  failure 
                        =>
                        (gen_fail(), work_list);

                    BIND (subst, dfa)                   #  guard 
                        =>
                        {   my (code, work_list)
                                =
                                walk (dfa, work_list);

                            namings
                                = 
                                subst::keyed_fold_backward 
                                    \\ (v, PVAR p, b) => (v, gen_path p) ! b;
                                       (v, VAR v', b) => b;
                                    end
                                    []
                                    subst;

                            (gen_let (gen_bind namings, code), work_list);
                        };

                    LET (path, (_, e), dfa)
                        =>
                        {   my (code, work_list)
                                =
                                walk (dfa, work_list);

                            (gen_let (gen_bind [(gen_variable path, e)], code), work_list);
                        };

                    WHERE (g, yes, no)
                        =>
                        {   my (yes, no, work_list)
                                =
                                expand_yes_no (yes, no, work_list);

                            (gen_if (g, yes, no), work_list);
                        };

                    CASE (path, cases, default)
                        =>
                        {   my (cases, work_list)
                                = 
                                list::fold_backward 
                                    (\\ ((con, paths, dfa), (cases, work_list))
                                        =
                                        {   my (code, work_list)
                                                =
                                                walk (dfa, work_list);

                                            ((con, map gen_pattern paths, code) ! cases, work_list); 
                                        }
                                    )
                                    ([], work_list)
                                    cases;


                            # Find the most common case
                            # and make it the default:
                            #
                            my (default, work_list)
                                = 
                                case default

                                     NULL
                                         =>
                                         (NULL, work_list);

                                     THE dfa
                                         => 
                                         {   my (code, work_list)
                                                 =
                                                 walk (dfa, work_list);

                                             (THE code, work_list);
                                         };
                                esac;

                            (gen_case (gen_variable path, cases, default), work_list);
                        };

                    SELECT (path, namings, body)
                        =>
                        {   my (body, work_list)
                                =
                                walk (body, work_list);

                            namings
                                =
                                map
                                    (\\ (p, v) =  (THE p, v))
                                    namings;

                            (gen_let([gen_proj (path, namings)], body), work_list);
                        };

                    CONT (k, body)
                        =>
                        {   my (body, work_list)
                                =
                                walk (body, work_list);

                            (gen_let([gen_cont (k, stamp, make_vars free_vars)], body), work_list);
                        };
                esac;


            # Generate code for the dfa;
            # accumulate all the auxiliary   
            # functions together and generate a let.
            #
            fun gen_all (root, dfa)
                =
                {   my (expression, work_list)
                        =
                        walk (dfa, empty_queue);


                    fun gen_aux_functions (([], []), funs)
                            =>
                            funs;   

                        gen_aux_functions (([], bbb), funs)
                             => 
                            gen_aux_functions ((reverse bbb,[]), funs);

                        gen_aux_functions ((dfa ! fff, bbb), funs)
                            =>
                            {   my (new_fun, work_list)
                                    =
                                    gen_new_fun (dfa, (fff, bbb));

                                gen_aux_functions (work_list, new_fun ! funs);
                            };
                    end;


                    root_decl
                        =
                        gen_val (gen_variable (PATH [INT 0]), root);

                    funs =  gen_aux_functions (work_list, []);


                    # Order the functions by dependencies;
                    # sort by lowest height:
                    #
                    funs =  lms::sort_list
                                #
                                (\\ ((h, _), (h', _)) =  h > h')
                                #
                                funs;

                    funs =  map #2 funs; 

                    gen_let (root_decl ! funs, expression);
                };

            gen_all (root, dfa);
        };

    };

end;                    # stipulate





Comments and suggestions to: bugs@mythryl.org

PreviousUpNext