You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
br-parser-tools/collects/mrspidey/Sba/hyper.ss

602 lines
21 KiB
Scheme

; hyper.ss - interface to GUI
; ----------------------------------------------------------------------
; Copyright (C) 1995-97 Cormac Flanagan
;
; This program is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License
; version 2 as published by the Free Software Foundation.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
; ----------------------------------------------------------------------
;;----------------------------------------------------------------------
(define (st:analyze-and-make-annotations filename)
(analysis-set-arrow-filter! #f)
(record-analyzed-file
filename
(lambda () (open-code-file filename))
(lambda () (analyze-program filename))))
(define (calc-annotations defs)
(match-let*
([type-annotations (calc-type-annotations defs)]
[(check-annotations uncheck-annotations) (calc-checks defs)]
[all-annotations
(vector type-annotations check-annotations uncheck-annotations)])
;;(pretty-debug `(links ,all-annotations))
all-annotations))
;;----------------------------------------------------------------------
(define arrow-filter #f) ; if not #f, then a list of templates
; only show pred with matching AV
(define (analysis-set-arrow-filter! C)
(set! arrow-filter
(if C
(map lookup-template (list C))
#f)))
(define (analysis-filter-on?)
(and arrow-filter (symbol->string (template-type (car arrow-filter)))))
(define (analysis-get-filters)
(cons
(cons "No filter" #f)
(map
(lambda (C) (cons (symbol->string C) C))
(append
`( nil num sym str void undefined true false
box cons vec eof iport oport
promise unit thread hash-table regexp parameterization semaphore)
(filter
(lambda (x) x)
(hash-table-map
constructor-env
(lambda (c template)
(and
(memq template-structure (template-super-templates template))
c))))))))
;; ------------------------------
(define (objs-contain-filter? objs)
(or
(and
(eq? arrow-filter #f)
(not (null? objs)))
(ormap
(lambda (AV)
(memq (AV-template AV) arrow-filter))
objs)))
(define (tvar-contain-filter-mvalues? get-Tvar-objs tvar)
(or
(objs-contain-filter? (get-Tvar-objs tvar))
(ormap
(match-lambda
[($ AV _ (? (eqc? template-mvalues)) _ #(tvar-mvlist))
;;(pretty-debug `(tvar-mvlist ,(Tvar-name tvar-mvlist)))
(ormap
(match-lambda
[($ AV _ (? (eqc? template-cons)) _ #(tvar-car-mvlist _))
(objs-contain-filter? (get-Tvar-objs tvar-car-mvlist))]
[_ #f])
(get-Tvar-objs tvar-mvlist))]
[_ #f])
(get-Tvar-objs tvar))))
(define (FlowType-contains-filter? ftype)
;(pretty-debug `(FlowType-contains-filter? ,(FlowType-name ftype)))
(or
(not arrow-filter)
(match (FlowType->Atype ftype)
[(or (? atconst?) (? atprim?) (? Tvar?))
(tvar-contain-filter-mvalues? Tvar-objs (FlowType->Tvar ftype))]
[($ atvalues (val1 . _))
(FlowType-contains-filter? val1)]
;; other atypes are atschema atthunk, atstruct, atunit, atlunit
;; all are too expensive to convert.
[x
(pretty-debug `(FlowType-contains-filter? unhandled-atype x))
#f])))
(define (FlowType-orig-objs-contains-filter? ftype)
;;(pretty-debug `(FlowType-orig-contains-filter? ,(FlowType-name ftype)))
(match (FlowType->Atype ftype)
[(or (? Tvar?))
(tvar-contain-filter-mvalues? Tvar-orig-objs (FlowType->Tvar ftype))]
[($ atvalues (val1 . _))
(FlowType-orig-objs-contains-filter? val1)]
;; other atypes are atschema atthunk, atstruct, atunit, atlunit
;; all are too expensive to convert.
[x
(pretty-debug `(FlowType-orig-objs-contains-filter? unhandled-atype x))
#f]))
;;----------------------------------------------------------------------
;; Abstracts over parents and children
(define analysis-walk1
(lambda (start in-get-edges file-visable?)
(assert (FlowType? start))
(pretty-debug `(analysis-walk1 ,(FlowType-name start)))
(if (string? (FlowType-type-annotation start))
;; start from .za file, show no edges
'()
(letrec*
( [traversed '()] ; list of FlowType's
[preds '()] ; visable FlowType's
[get-edges
(lambda (ftype)
(let ([r (in-get-edges ftype)])
(pretty-debug `(get-edges ,(map FlowType-name (cons ftype r))))
r))]
[traverse
(lambda (ftype)
(pretty-debug `(traverse ,(FlowType-name ftype)))
(when
(and
(FlowType-contains-filter? ftype)
(not (memq ftype traversed)))
(set! traversed (cons ftype traversed))
(let*
( [ftype-w/-ta (get-ftype-w/-ta ftype)]
[ta (and ftype-w/-ta
(FlowType-type-annotation ftype-w/-ta))])
(pretty-debug
`(traverse-ftype-w/-ta
,(and ftype-w/-ta (FlowType-name ftype-w/-ta))))
(cond
[(not ta)
;; invisable
;; Go back to parents
(for-each traverse (get-edges ftype))]
[(string? ta)
(if (file-visable? ta)
;; ftype from .za file, source file is loaded
;; can follow this edge unless dest is from same file
(for-each
(lambda (ftype2)
(let ([ta2 (FlowType-type-annotation ftype2)])
(unless (and (string? ta2) (string=? ta ta2))
(traverse ftype2))))
(get-edges ftype))
;; .za file, source file not loaded
(set! preds (cons ftype-w/-ta preds)))]
[(and
(not (string? ta))
(not (eq? ftype-w/-ta start)))
;; visable
;; add to preds unless src and dest are single value
(set! preds (cons ftype-w/-ta preds))]
[else
;; Invisable
;; Go back to parents
(for-each traverse (get-edges ftype))]))))])
(for-each traverse (get-edges start))
(pretty-debug `(traversed ,(map FlowType-name traversed)))
(pretty-debug
`(preds ,(map
(lambda (i) (and (FlowType? i) (FlowType-name i)))
preds)))
(remq start preds)))))
(define (get-ftype-w/-ta ftype)
(if (FlowType-type-annotation ftype)
ftype
(and (FlowType-values-ftype ftype)
(get-ftype-w/-ta (FlowType-values-ftype ftype)))))
(define (single-value-ftype ftype)
(let ([r
(match (FlowType->Atype ftype)
[(? Tvar? tvar)
(ormap
(lambda (AV) (not (eq? (AV-template AV) template-mvalues)))
(get-Tvar-objs tvar))]
[($ atvalues) #f]
[_ #t])])
(pretty-debug `(single-value-ftype ,(FlowType-name ftype) ,r))
r))
;;----------------------------------------------------------------------
;; Returns the preds in the Tvar graph of a given ftype
;; format is a list of Tvars of predecessors that are visable
(define analysis-parents
(lambda (ftype file-visable?)
(pretty-debug `(analysis-parents ,(FlowType-name ftype) ,(print-struct)))
(let ([r (analysis-walk1 ftype get-arrowfrom file-visable?)])
(pretty-debug `(analysis-parents-returns ,(map FlowType-name r)))
r)))
(define (get-arrowfrom to)
(append
(if (Tvar? to)
(append
(get-mvalue-components to)
(Tvar-edgefrom to))
'())
(match (FlowType->Atype to)
[($ atvalues ftypes)
ftypes]
[_ '()])
(FlowType-arrowfrom to)))
(define (get-mvalue-components tvar)
'(pretty-debug `(get-mvalue-components ,(Tvar-name tvar)))
(filter-map
(lambda (AV)
(if (eq? (AV-template AV) template-mvalues)
;; get-nth
(let ( [p (mk-Tvar 'p)]
[l (mk-Tvar 'l)])
(new-edge! (vector-ref (AV-fields+ AV) 0) p)
(new-con! p (make-con-cdr p))
(new-con! p (make-con-car l))
'(pretty-debug
`(get-mvalue-components
,(Tvar-name tvar) ,(Tvar-name p) ,(Tvar-name l)))
l)
#f))
(get-Tvar-objs tvar)))
;; ----------------------------------------------------------------------
(define analysis-children
(lambda (ftype file-visable?)
(analysis-walk1
ftype
FlowType-alledgeto
file-visable?)))
(define FlowType-alledgeto
(lambda (ftype)
(append
;; Direct successors
(FlowType-arrowto ftype)
(if (FlowType-values-ftype ftype)
(list (FlowType-values-ftype ftype))
'())
;; For filter successors, make sure some same values
(if (Tvar? ftype)
(append
(Tvar-edgeto ftype)
(filter-map
(match-lambda
[($ con-filter _ _ tvar) tvar]
[_ #f])
(Tvar-constraints ftype)))
'()))))
;;----------------------------------------------------------------------
;; Returns all ancestor/descendant arrows of a given ftype
;; as (listof (list from to))
(define analysis-walkn
(lambda (ftype get-edges file-visable?)
(assert (FlowType? ftype))
(pretty-debug `(analysis-walkn ,(FlowType-name ftype)))
;;(error)
(let ( [done '()] ; list of FlowType's
[arrows '()]) ; visable arrows
(recur traverse ([ftype ftype])
(unless (memq ftype done)
(set! done (cons ftype done))
(let ([parents (analysis-walk1 ftype get-edges file-visable?)])
(for-each
(lambda (p)
(set! arrows (cons (list p ftype) arrows))
(traverse p))
parents))))
arrows)))
;;----------------------------------------------------------------------
;; Returns all ancestor arrows of a given ftype
;; as (listof (list from to))
(define analysis-ancestors
(lambda (ftype file-visable?)
(analysis-walkn ftype get-arrowfrom file-visable?)))
(define analysis-descendants
(lambda (ftype file-visable?)
(map reverse
(analysis-walkn ftype FlowType-alledgeto file-visable?))))
;;----------------------------------------------------------------------
;; Calcs the shortest path to a source FlowType with
;; orig-objs matching filter via breadth-first search
;; Returns a list of FlowTypes
;; or nil if no path to visable source
;; or #f if ftype does not contain filter
(define analysis-shortest-path
(lambda (ftype file-visable?)
(assert (FlowType? ftype))
(if (FlowType-contains-filter? ftype)
(let ([visited (list ftype)])
(letrec
([traverse
(lambda (list-paths)
(pretty-debug
`(list-paths
,(map (lambda (p) (map FlowType-name p)) list-paths)
,(map FlowType-name visited)))
(match list-paths
[()
;; No paths -> return the empty path
'()]
[((and path (ftype . rest-path)) . rest-paths)
(pretty-debug
`(path ,(map FlowType-name path)
rest-paths
,(map (lambda (p) (map FlowType-name p)) rest-paths)))
(let* ( [parents1 (analysis-parents ftype file-visable?)]
[parents2
(filter
(lambda (p)
(if (memq p visited)
#f
(begin
(set! visited (cons p visited))
#t)))
parents1)])
(set! visited (cons ftype visited))
(if (and (null? parents2)
(or (null? parents1)
(null? rest-paths)))
;; either this path has terminated,
;; or went to cycle and is only one left.
;; either way, return it
(reverse path)
(traverse
(append rest-paths
(filter-map
(lambda (parent) (cons parent path))
parents2)))))]))])
(let ([path (traverse
(map list (analysis-parents ftype file-visable?)))])
(pretty-debug `(traverse-path ,(map FlowType-name path)))
path)))
#f)))
'(define analysis-shortest-path
(lambda (ftype file-visable?)
(assert (FlowType? ftype))
(if (FlowType-contains-filter? ftype)
(let ([visited '()])
(letrec
([traverse
(lambda (list-paths)
(pretty-debug
`(list-paths
,(map (lambda (p) (map FlowType-name p)) list-paths)
,(map FlowType-name visited)))
(match list-paths
[()
;; No paths -> return the empty path
'()]
[((and path (ftype . rest-path)) . rest-paths)
(pretty-debug
`(path ,(map FlowType-name path)
rest-paths
,(map (lambda (p) (map FlowType-name p)) rest-paths)))
(if (memq ftype visited)
;; going around a loop
(traverse rest-paths)
(let* ( [parents (analysis-parents ftype file-visable?)])
(set! visited (cons ftype visited))
(if (and (null? parents) (null? rest-paths))
;; this path has terminated, and is the only one left,
;; and hence the longest, so return it
(reverse path)
(traverse
(append rest-paths
(filter-map
(lambda (parent) (cons parent path))
parents))))))]))])
(let ([path (traverse
(map list (analysis-parents ftype file-visable?)))])
(pretty-debug `(traverse-path ,(map FlowType-name path)))
path)))
#f)))
'(define analysis-shortest-path
(lambda (ftype file-visable?)
(assert (FlowType? ftype))
(let ([visited '()])
(letrec
([traverse
(lambda (list-paths)
(pretty-debug
`(list-paths
,(map (lambda (p) (map FlowType-name p)) list-paths)
,(map FlowType-name visited)))
(match list-paths
[()
;; No paths -> return the empty path
'()]
[((and path (ftype . rest-path)) . rest-paths)
(if (memq ftype visited)
;; Visited on a shorter path
(traverse rest-paths)
(begin
(pretty-print
`(path ,(map FlowType-name path)
rest-paths
,(map (lambda (p) (map FlowType-name p)) rest-paths)))
(set! visited (cons ftype visited))
(cond
[(FlowType-orig-objs-contains-filter? ftype)
;; Has orig-objs that match the filter
;; => return this path
(reverse path)]
[(FlowType-contains-filter? ftype)
;; Has propogated objs that match the filter
;; => go back to parent
(let* ([new-paths
(map
(lambda (parent) (cons parent path))
(get-arrowfrom ftype))])
(pretty-print
`(rest-paths ,(map (lambda (p) (map FlowType-name p)) rest-paths)
new-paths
,(map (lambda (p) (map FlowType-name p)) new-paths)))
(traverse (append rest-paths new-paths)))]
[else
;; This FlowType no good
(traverse rest-paths)])))]))])
(let ([path (traverse (list (list ftype)))])
(pretty-print `(traverse-path ,(map FlowType-name path)))
;; Drop first FlowType - it is us
;; Remove invisable FlowType's
;; NO LONGER Add '() as last tag if last FlowType invisable
(assert (or (null? path) (eq? (car path) ftype)))
(if (null? path)
'()
(recur loop ([path (cdr path)])
(match path
[(ftype . rest)
(cond
[(FlowType-type-annotation ftype) (cons ftype (loop rest))]
[(null? rest) '()]
[else (loop rest)])]
[()
;; No path
'()]))))))))
;; ======================================================================
;; Returns a string representation of the the type
(define (FlowType->SDL-text ftype)
(let ([type-port (open-output-string)])
(dynamic-let
([pretty-print-columns (st:pretty-type-width)])
(pretty-print (FlowType->SDL ftype) type-port))
(begin0
(get-output-string type-port)
(close-output-port type-port))))
(define analysis-callback
(lambda (ftype)
(assert (FlowType? ftype) 'analysis-callback ftype)
(pretty-debug `(analysis-callback ,(FlowType->pretty ftype)))
(FlowType->SDL-text ftype)))
;;----------------------------------------------------------------------
'(define test-hyper
(lambda (files)
(initialize-analysis!)
(match-let*
([(defs in out)
(sba-analyze-file (files->file-thunk* files))])
(set! global-in-env in-env)
(set! global-out-env out-env)
(report-unbound-vars)
(hyper defs)
(printf "Testing parents~n")
(pretty-print (mapLR analysis-parents list-ftype))
(printf "Testing analysis-shortest-path~n")
(pretty-print (mapLR analysis-shortest-path list-ftype))
(printf "Testing children~n")
(pretty-print (mapLR analysis-children list-ftype list-ftype))
(void))))
;; (trace analysis-parents analysis-shortest-path analysis-children)
;;----------------------------------------------------------------------
;; zodiac-based source-correlating pretty-printer
'(define (correlate-pretty-print sexp)
(let* ([p (open-output-string)]
[_ (pretty-print sexp p)]
[s (get-output-string p)]
[_ (close-output-port p)]
[p (open-input-string p)]
[r (zodiac:read p)]
[zexp (r)])
(values s
(recur loop ([sexp sexp][zexp zexp])
(match (list sexp zexp)
[((? list? sl) ($ zodiac:list _ s f (? list? zl)))
(list
(zodiac:location-offset s)
(zodiac:location-offset f)
(map loop sl zl))]
[(sexp ($ zodiac:zodiac _ s f))
(list
(zodiac:location-offset s)
(zodiac:location-offset f)
sexp)])))))
;; ----------------------------------------------------------------------
'(define analysis-get-pred-filter-list
(lambda ()
(cons (cons "All sources" #f)
(reverse
(map
(match-lambda
[(con . constructor) (cons (symbol->string con) (list con))])
(append constructor-env default-constructor-env))))))
(define analysis-get-param
(lambda (param)
((eval param))))
(define analysis-set-param!
(lambda (param val)
((eval param) val)))
;; ----------------------------------------------------------------------
;; ----------
'(define (contains-some-same-vals? ftype value-from-ftypes)
;; Returns #t if ftype contains at least some values from
;; each of value-from-ftypes
(andmap
(lambda (value-from-ftype)
(match (list
(FlowType->Atype ftype)
(FlowType->Atype value-from-ftype))
[((? fo-Atype? x) (? fo-Atype? y)) (eq? x y)]
[((? Tvar? x) (? Tvar? y))
(ormap
(lambda (AV) (memq AV (get-Tvar-objs y)))
(get-Tvar-objs x))]
[_ #t]))
value-from-ftypes))