;;;; Copyright 2002, Jonathan Bachrach.  See file TERMS.

;;;;;======================================================================
;;;;;  Dependency Tracking
;;;;;======================================================================
;;;;;  TODO - Considering renaming all the major classes in this module.

(use goo)

;;;;=======================================================================
;;;;  Types of Dependencies
;;;;=======================================================================
;;;;  We represent dependency types as integers for the sake of peformance
;;;;  and compactness.

(dv <dependency-type> <int>)

(df dependency-or (dep1|<dependency-type>
                   dep2|<dependency-type>
                   => <dependency-type>)
  (| dep1 dep2))

(df dependency-and (dep1|<dependency-type>
                    dep2|<dependency-type>
                    => <dependency-type>)
  (& dep1 dep2))

(dv $nul-dependency 0)
(dv $all-dependency-types 65535) ;; XXX - Should be all 1 bits.

(df dependency-nul? (dep|<dependency-type> => <log>)
  (== dep $nul-dependency))

(df dependency-includes-all? (main|<dependency-type>
                              values|<dependency-type>
                              => <log>)
  (== (dependency-and main values) values))

(df dependency-includes-any? (main|<dependency-type>
                              values|<dependency-type>
                              => <log>)
  (not (dependency-nul? (dependency-and main values))))

(export
  <dependency-type>
  dependency-or
  dependency-and
  $nul-dependency
  $all-dependency-types
  dependency-nul?
  dependency-includes-all?
  dependency-includes-any?)


;;;;=======================================================================
;;;;  Parse-Time Dependencies
;;;;=======================================================================
;;;;  If these are invalidated, we need to reparse the source code.

(dv $name-parse-dependency 1)         ; We translated a name to a binding.
(dv $expansion-parse-dependency 2)    ; We expanded a macro.

(dv $parse-dependencies
  (dependency-or $name-parse-dependency
                 $expansion-parse-dependency))

(export
  $name-parse-dependency
  $expansion-parse-dependency
  $parse-dependencies)


;;;;=======================================================================
;;;;  Optimization-Time Dependencies
;;;;=======================================================================
;;;;  If these are invalidated, we only need to re-optimize the source
;;;;  code.

(dv $value-optimization-dependency 4) ; We looked at the binding's value.

(dv $optimizaton-dependencies
  $value-optimization-dependency)

(export
  $value-optimization-dependency
  $optimizaton-dependencies)


;;;;=======================================================================
;;;;  Dependents & Dependables
;;;;=======================================================================

;;; May be depended-upon by a <dependent>.
(dc <dependable> (<any>))
  ;; A table keyed by <dependent>, containing <dependency-type> values.
  (dp dependents (<dependable> => <tab>) (fab <tab> 4))

;;; May depend upon a <dependable>.
(dc <dependent> (<any>))
  (dp! depends-on (<dependent> => <tab>) (fab <tab> 20))

;;; Log a dependency, together with any type information.
(df log-dependency (dependable|<dependable>
                    dependent|<dependent>
                    dtype|<dependency-type> => #f)
  (set (elt (depends-on dependent) dependable) #t)
  (def dep-table (dependents dependable))
  (def entry     (elt-or dep-table dependent #f))
  (set (elt dep-table dependent)
       (if entry
           (dependency-or entry dtype)
           dtype))
  #f)

;;; Look up a dependecy.  Primarily used for testing.
(df find-dependency (dependable|<dependable>
                     dependent|<dependent>
                     => (t? <dependency-type>))
  (elt-or (dependents dependable) dependent #f))

;;; Clobber a single dependent.  'dtype' says which kinds of dependencies
;;; have been invalidated.
(dg invalidate-dependent (dependent|<dependent>
                          dependable|<dependable>
                          dtype|<dependency-type>))

;;; Clobber all dependents of 'dependable' which rely on any of the
;;; bits in 'invalid-dtype'.
(df invalidate-dependents
    (dependable|<dependable> invalid-dtype|<dependency-type>)
  (do-keyed
   (fun (dependent dependendent-dtype)
     (def intersection (dependency-and invalid-dtype dependendent-dtype))
     (unless (dependency-nul? intersection)
       (invalidate-dependent dependent dependable intersection)))
   (dependents dependable)))

;;; Remove a dependent from everything it depends upon.
(df detach-dependent (dependent|<dependent>)
  (do-keyed
   (fun (dependable junk)
     (unless (del! (dependents dependable) dependent)
       (ast-error "Cannot remove %= from %=.\n" dependent dependable)))
   (depends-on dependent))
  (set (depends-on dependent) (fab <tab> 0)))

(dg ast-error (msg args|...))

(export
  ast-error
  <dependable>
  <dependent>
  log-dependency
  find-dependency
  invalidate-dependent
  invalidate-dependents
  detach-dependent)
