Skip to content

Commit 8dfc1e2

Browse files
maxsnewrfindler
authored andcommitted
Fix infinite loop in certain recursive enumerations
Key is to always put a productive production first, since this is the first case of or/e. closes #186
1 parent b12e410 commit 8dfc1e2

File tree

2 files changed

+87
-69
lines changed

2 files changed

+87
-69
lines changed

redex-lib/redex/private/preprocess-lang.rkt

Lines changed: 79 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
racket/match
99
racket/set
1010
racket/promise
11-
"build-nt-property.rkt"
1211
"lang-struct.rkt"
1312
"match-a-pattern.rkt")
1413

@@ -44,18 +43,15 @@
4443
(set-member? cyclic-nts (nt-name nt)))
4544
lang))
4645
;; topological sort
47-
(define sorted-left
46+
(define sorted-finite
4847
(topo-sort non-cyclic
4948
(filter-edges edges non-cyclic)))
5049
;; rhs sort
51-
(define sorted-right
52-
(sort-productions cyclic
53-
cyclic-nts
54-
clang-all-ht/f))
55-
56-
(values sorted-left
57-
sorted-right
58-
(build-cant-enumerate-table lang edges)))
50+
(define-values (sorted-cyclic unsolvables)
51+
(sort-productions cyclic clang-all-ht/f))
52+
(values sorted-finite
53+
sorted-cyclic
54+
(build-cant-enumerate-table lang edges unsolvables)))
5955

6056
;; find-edges : lang -> (hash[symbol] -o> (setof (listof symbol)))
6157
(define (find-edges lang)
@@ -105,7 +101,7 @@
105101
(hash)
106102
lang))
107103

108-
;; find-cycles : (hash[symbol] -o> (setof symbol)) -> (setof symbol)
104+
;; find-cycles : (hash[symbol] -o> (setof symbol)]) -> (setof symbol)
109105
(define (find-cycles edges)
110106
(foldl
111107
(λ (v s)
@@ -190,61 +186,76 @@
190186
lang))
191187

192188

193-
;; sort-productions : lang,
194-
;; (hash[symbol] -o> (setof symbol))
195-
;; (or/c #f (hash[symbol -o> (list/c any)])) -> lang
196-
(define (sort-productions cyclic nts clang-all-ht/f)
197-
(define table (terminal-distance-table cyclic nts))
198-
(for/list ([cur-nt (in-list cyclic)])
199-
(match cur-nt
200-
[(nt name productions)
201-
(define (max-terminal-distance pat)
202-
(define referenced-nts (directly-used-nts pat))
203-
(define maximum
204-
(for/max ([cur-name (in-set referenced-nts)])
205-
(if (symbol=? cur-name name)
206-
+inf.0
207-
(hash-ref table cur-name 0))))
208-
(if (and (negative? maximum)
209-
(infinite? maximum))
210-
0
211-
maximum))
212-
(define production-vec (apply vector productions))
213-
(define permutation
214-
(sort (build-list (vector-length production-vec) values)
215-
<
216-
#:key (compose max-terminal-distance
217-
rhs-pattern
218-
(λ (i) (vector-ref production-vec i)))
219-
#:cache-keys? #t))
220-
(when clang-all-ht/f
221-
(define clang-all-ht-nt-vec (apply vector (hash-ref clang-all-ht/f name)))
222-
(hash-set! clang-all-ht/f name
223-
(for/list ([i (in-list permutation)])
224-
(vector-ref clang-all-ht-nt-vec i))))
225-
(nt name
226-
(for/list ([i (in-list permutation)])
227-
(vector-ref production-vec i)))])))
189+
;; Problem: we need to find an ordering of the productions of each of
190+
;; the metavariables such that the graph induced by the first
191+
;; productions in each case has no cycles.
192+
;; spanning-tree : HyperGraph -> (Listof (List Index (Setof NTName))) (Listof NTName)
193+
(define (spanning-tree hg)
194+
(define init-vertices (hash-keys hg))
195+
(let loop ([edges (hash)]
196+
[vertices init-vertices]
197+
[time (length init-vertices)])
198+
(cond
199+
[(zero? time)
200+
(values edges vertices)]
201+
[else
202+
(match-define (cons v vs) vertices)
203+
(define good-edge
204+
(findf (λ (e) (andmap (λ (v2) (not (member v2 vertices))) (set->list (second e))))
205+
(hash-ref hg v)))
206+
(cond [good-edge
207+
(loop (hash-set edges v good-edge)
208+
vs
209+
(length vs))]
210+
[else
211+
(loop edges (append vs (list v)) (sub1 time))])])))
212+
213+
;; A HyperGraph is a Hash NTName (Listof (List Index (Setof NTName)))
214+
;; associating each non-terminal to a list of out-going edges
215+
(define (hypergraph lang)
216+
(for/hash ([nt (in-list lang)])
217+
(define out-edges
218+
(for/list ([i (in-naturals)]
219+
[rhs (in-list (nt-rhs nt))])
220+
(list i (directly-used-nts (rhs-pattern rhs)))))
221+
(values (nt-name nt) out-edges)))
222+
223+
;; sort-productions : lang (or/c #f (hash[symbol -o> (list/c any)]))
224+
;; -> lang
225+
;; sorts the language
226+
;; SIDE EFFECT: if clang-all-ht/f is not #f, sorts it
227+
(define (sort-productions lang clang-all-ht/f)
228+
(define-values (spanner unsolvables) (spanning-tree (hypergraph lang)))
229+
(define sorted
230+
(for/list ([cur-nt (in-list lang)])
231+
(match cur-nt
232+
[(nt name productions)
233+
(cond
234+
[(hash-has-key? spanner name)
235+
(define the-edge (first (hash-ref spanner name)))
236+
237+
;; less than if the left is the chosen one and the right is not
238+
(define (less-than? i1 i2)
239+
(and (equal? i1 the-edge)
240+
(not (equal? i2 the-edge))))
241+
242+
(define production-vec (apply vector productions))
243+
(define permutation
244+
(sort (build-list (vector-length production-vec) values)
245+
less-than?
246+
#:cache-keys? #t))
247+
(when clang-all-ht/f
248+
(define clang-all-ht-nt-vec (apply vector (hash-ref clang-all-ht/f name)))
249+
(hash-set! clang-all-ht/f name
250+
(for/list ([i (in-list permutation)])
251+
(vector-ref clang-all-ht-nt-vec i))))
252+
(nt name
253+
(for/list ([i (in-list permutation)])
254+
(vector-ref production-vec i)))]
255+
[else (nt name productions)])])))
256+
(values sorted unsolvables))
228257

229-
;; terminal-distance-table : lang (hash[symbol] -o> symbol)
230-
;; -> (hash[symbol] -o> (U natural +inf)
231-
(define (terminal-distance-table cyclic recs)
232-
(define (terminal-distance pat this-nt-name table)
233-
(define referenced-nts (directly-used-nts pat))
234-
(define maximum
235-
(for/max ([cur-name (in-set referenced-nts)])
236-
(cond [(symbol=? cur-name this-nt-name)
237-
+inf.0]
238-
[else
239-
(hash-ref table cur-name 0)])))
240-
(or (and (infinite? maximum)
241-
(negative? maximum)
242-
0)
243-
(add1 maximum)))
244-
(build-nt-property/name cyclic
245-
terminal-distance
246-
+inf.0
247-
min))
258+
;; A NTName is a symbol representing the name of a non-terminal
248259

249260
;; directly-used-nts : pat -> (setof symbol)
250261
(define (directly-used-nts pat)
@@ -347,8 +358,7 @@
347358
(my-max current-max
348359
(let () . defs+exprs))))]))
349360

350-
351-
(define (build-cant-enumerate-table lang edges)
361+
(define (build-cant-enumerate-table lang edges unsolvables)
352362
;; cant-enumerate-table : hash[sym[nt] -o> boolean]
353363
(define cant-enumerate-table (make-hash))
354364

@@ -381,7 +391,8 @@
381391
;; fill in the entire table
382392
(for ([nt (in-list lang)])
383393
(cant-enumerate-nt/fill-table (nt-name nt)))
384-
394+
(for ([name (in-list unsolvables)])
395+
(hash-set! cant-enumerate-table name #t))
385396
cant-enumerate-table)
386397

387398
;; can-enumerate? : any/c hash[sym -o> any[boolean]] (promise hash[sym -o> any[boolean]])

redex-test/redex/tests/enum-test.rkt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,11 @@
214214
(lambda ()
215215
((generate-term nested-finite-cross (cross b) #:i-th)
216216
1)))
217-
(try-it nested-finite-cross (cross b)))
217+
(try-it nested-finite-cross (cross b)))
218+
219+
(let ()
220+
(define-language L
221+
(m ::= (e))
222+
(e ::= (f) 1)
223+
(f ::= e (e ...)))
224+
(try-it L m))

0 commit comments

Comments
 (0)