-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathcompute-length.lisp
88 lines (72 loc) · 2.64 KB
/
compute-length.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
(in-package :cl-string-generator)
(defvar *max-length*)
(defvar *registers*)
(defun minimum (range)
(if (null range)
0
(loop :for elt :in range
:minimize (if (consp elt)
(car elt)
elt))))
(defun maximum (range)
(if (null range)
*max-length*
(loop :for elt :in range
:maximize (if (consp elt)
(cdr elt)
elt))))
(defgeneric add-range (x y)
(:method ((x integer) (y integer))
(+ x y))
(:method ((x integer) (y cons))
(cons (+ x (car y)) (+ x (cdr y))))
(:method ((x cons) (y integer))
(cons (+ (car x) y) (+ (cdr x) y)))
(:method ((x cons) (y cons))
(cons (+ (car x) (car y)) (+ (cdr x) (cdr y)))))
(defun all-length-candidates (range)
(let ((acc '()))
(labels ((f (range sum)
(if (null range)
(pushnew sum acc :test #'equal)
(dolist (n (first range))
(f (rest range) (add-range n sum))))))
(f range 0)
(nreverse acc))))
(defgeneric compute-range-of-length-aux (regex))
(defmethod compute-range-of-length-aux ((regex void))
(list 0))
(defmethod compute-range-of-length-aux ((regex str))
(list (length (.str regex))))
(defmethod compute-range-of-length-aux ((regex seq))
(all-length-candidates
(loop :for regex-1 :in (.seq regex)
:for range := (compute-range-of-length-aux regex-1)
:collect range)))
(defmethod compute-range-of-length-aux ((regex alternation))
(loop :for regex-1 :in (.choices regex)
:for range := (compute-range-of-length-aux regex-1)
:append range))
(defmethod compute-range-of-length-aux ((regex repetition))
(let ((range (compute-range-of-length-aux (.regex regex))))
(list (cons (* (minimum range)
(.minimum regex))
(* (maximum range)
(.maximum regex))))))
(defmethod compute-range-of-length-aux ((regex register))
(let ((range (compute-range-of-length-aux (.regex regex))))
(setf (gethash (.number regex) *registers*) range)
range))
(defmethod compute-range-of-length-aux ((regex back-reference))
(gethash (.number regex) *registers*))
(defmethod compute-range-of-length-aux ((regex random-char))
(list 1))
(defun compute-range-of-length (regex)
(let ((*registers* (make-hash-table)))
(compute-range-of-length-aux regex)))
(defun compute-fix-length (regex *max-length*)
(let ((range (compute-range-of-length regex)))
(mean (loop :for elt :in range
:collect (if (consp elt)
(cdr elt)
elt)))))