| 24 | | opr ∩ (r0: Range[\ZZ32\], r1: Range[\ZZ32\]) = (r0.lower MAX r1.lower) : (r0.upper MIN r1.upper) |
| | 24 | opr ∩ (r0: Range⟦ZZ32⟧, r1: Range⟦ZZ32⟧) = (r0.lower MAX r1.lower) : (r0.upper MIN r1.upper) |
| | 25 | |
| | 26 | test rangeIntersect() = do |
| | 27 | assert((0:5) ∩ (0:3), 0:3) |
| | 28 | assert((0:5) ∩ (3:7), 3:5) |
| | 29 | assert((0:5) ∩ (7:10), 7:5) |
| | 30 | assert((5:3).size, 0, "The size of 5:3 is not zero!") |
| | 31 | assert((9:8).size, 0, "The size of 9:8 is not zero!") |
| | 32 | assert( (5:3).isEmpty, "5:3 is not empty!") |
| | 33 | assert( (9:8).isEmpty, "9:8 is not empty!") |
| | 34 | assert( ((0:5) ∩ (7:10)).isEmpty, "0:5 has a non-empty intersection with 7:10 !") |
| | 35 | assert( (7:9)≪7, (0#3), "7:9≪7 isn't 0#3" ) |
| | 36 | end |
| | 37 | |
| 26 | | opr ↙(r: Range[\ZZ32\], leftShift: ZZ32) = (r.lower - leftShift)#r.extent |
| 27 | | |
| | 39 | opr ≪(r: Range⟦ZZ32⟧, leftShift: ZZ32) = (r.lower - leftShift)#r.extent |
| | 40 | (* Shift a range right by the specified amount. *) |
| | 41 | opr ≫(r: Range⟦ZZ32⟧, rightShift: ZZ32) = (r.lower + rightShift)#r.extent |
| | 42 | |
| | 43 | test testShiftLeft(): () = do |
| | 44 | assert( (10:12) ≪ 9, 1:3) |
| | 45 | end |
| | 46 | |
| | 47 | test testShiftRight(): () = do |
| | 48 | assert( (2:3) ≫ 5, 7:8) |
| | 49 | end |
| | 50 | |
| | 51 | test testRangeContainment = do |
| | 52 | assert( (2:3) ≤ (2:3), "(2:3) not less than or equal to (2:3)") |
| | 53 | (* println "(2#2) CMP (2:3) = " ((2#2) CMP (2:3)) *) |
| | 54 | assert( (2#2) ≤ (2:3), "(2#2) not less than or equal to (2:3)") |
| | 55 | end |
| | 56 | (* |
| | 57 | ==== Shared Traits ==== |
| | 58 | *) |
| | 59 | |
| | 60 | trait Concatenable extends String |
| | 61 | opr || (self, other:String): String = |
| | 62 | if |other| = 0 then self else CatString(self, other) end |
| | 63 | opr || (self, _:EmptyString): String = self |
| | 64 | opr || (self, other:Char) = CatString(self, other.toString) |
| | 65 | end |
| 80 | | if leftBounds.isEmpty then right.uncheckedSubstring(rightBounds') |
| 81 | | else if rightBounds.isEmpty then left.uncheckedSubstring(leftBounds') |
| 82 | | else left.uncheckedSubstring(leftBounds') || right.uncheckedSubstring(rightBounds') |
| 83 | | end |
| 84 | | *) |
| 85 | | |
| 86 | | |
| 87 | | opr || (self, other:String): String = |
| 88 | | if |other| = 0 then self else CatString(self, other) end |
| 89 | | opr || (self, _:EmptyString) = self |
| 90 | | opr || (self, other:Char) = CatString(self, other.toString) |
| 91 | | |
| | 122 | (* atomic do |
| | 123 | println "uncheckedSubstring " r0 " of " |
| | 124 | self.showStructure() |
| | 125 | println "===============" |
| | 126 | println "leftBounds' = " leftBounds' ( if leftBounds'.isEmpty then "(empty)" else "(non-empty)" end ) |
| | 127 | println "rightBounds' = " rightBounds' ( if rightBounds'.isEmpty then "(empty)" else "(non-empty)" end ) |
| | 128 | println "===============" |
| | 129 | end *) |
| | 130 | if leftBounds'.isEmpty then |
| | 131 | right.uncheckedSubstring(rightBounds') |
| | 132 | elif rightBounds'.isEmpty then |
| | 133 | left.uncheckedSubstring(leftBounds') |
| | 134 | else |
| | 135 | SubString'(self, r0) |
| | 136 | end |
| | 137 | end |
| | 138 | |
| 94 | | |
| 95 | | end |
| 96 | | |
| 97 | | object ConcatGenerator[\T\](first:Generator[\T\], second:Generator[\T\]) |
| 98 | | extends Generator[\T\] |
| 99 | | generate[\R\](r: Reduction[\R\], body:T->R):R = |
| 100 | | r.join(first.generate[\R\](r, body), second.generate[\R\](r, body)) |
| | 141 | |
| | 142 | subdivide(): Maybe⟦Generator⟦(ZZ32, String)⟧⟧ = Just ⟨ (0, left), (|left|, right) ⟩ |
| | 143 | |
| | 144 | end |
| | 145 | |
| | 146 | object ConcatGenerator⟦T⟧(first:Generator⟦T⟧, second:Generator⟦T⟧) |
| | 147 | extends Generator⟦T⟧ |
| | 148 | generate⟦R⟧(r: Reduction⟦R⟧, body:T->R):R = |
| | 149 | r.join(first.generate⟦R⟧(r, body), second.generate⟦R⟧(r, body)) |
| 147 | | |
| 148 | | run(args: String...): () = do println "Finished" end |
| 149 | | |
| 150 | | test rangeIntersect() = do |
| 151 | | assert((0:5) ∩ (0:3), 0:3) |
| 152 | | assert((0:5) ∩ (3:7), 3:5) |
| 153 | | assert((0:5) ∩ (7:10), 7:5) |
| 154 | | deny(5:3, 9:8) (* I hoped that that this would be an assert! *) |
| 155 | | assert((5:3).size, 0, "The size of 5:3 is not zero!") |
| 156 | | assert((9:8).size, 0, "The size of 9:8 is not zero!") |
| 157 | | assert( (5:3).isEmpty, "5:3 is not empty!") |
| 158 | | assert( (9:8).isEmpty, "9:8 is not empty!") |
| 159 | | assert( ((0:5) ∩ (7:10)).isEmpty, "0:5 has a non-empty intersection with 7:10 !") |
| | 194 | |
| | 195 | subdivide() = Nothing⟦Generator⟦(ZZ32, String)⟧⟧ |
| | 196 | |
| | 197 | (* |
| | 198 | ==== SubString ==== |
| | 199 | *) |
| | 200 | trait SubString extends {String, Concatenable} |
| | 201 | comprises {SubString'} |
| | 202 | end |
| | 203 | |
| | 204 | object SubString'(parent: String, range: Range⟦ZZ32⟧) extends SubString |
| | 205 | getter size() = range.size |
| | 206 | getter depth() = parent.depth (* Why not one more than the parent? Would this break the balancing invariant? *) |
| | 207 | getter isEmpty() = false |
| | 208 | |
| | 209 | opr CMP(self, other: String) = do |
| | 210 | (* if pgen ← parent.subdivide() then |
| | 211 | BIG LEXICO [(start, str) ← pgen] self.uncheckedSubstring((start#|str|) ∩ self.bounds) CMP str |
| | 212 | else |
| | 213 | *) |
| | 214 | (BIG LEXICO [i ← 0#(|self| MIN |other|)] self.get(i) CMP other.get(i)) LEXICO (|self| CMP |other|) |
| | 215 | (* end |
| | 216 | *) |
| | 217 | end |
| | 218 | |
| | 219 | |
| | 220 | verify() = do |
| | 221 | parent.verify() |
| | 222 | deny(range.isEmpty, "SubString (" range ") has empty range") |
| | 223 | assert(range < parent.bounds, "SubString (" range ") has range equal to or greater than that of parent (" parent.bounds ")") |
| | 224 | end |
| | 225 | showStructure(indent) = do |
| | 226 | margin(indent) |
| | 227 | println "S" |self| "=[" range "]" |
| | 228 | parent.showStructure(indent + 8) |
| | 229 | end |
| | 230 | |
| | 231 | get(i:ZZ32) = do |
| | 232 | assert(i < range.size, "getting char " i " from a substring of size " |range|) |
| | 233 | parent.get(range.lower+i) |
| | 234 | end |
| | 235 | |
| | 236 | uncheckedSubstring(r0: Range⟦ZZ32⟧) = do |
| | 237 | assert((r0≫range.lower) ≤ range, "asking for substring [" r0 "] of S" self.size) |
| | 238 | if r0.isEmpty then EmptyString |
| | 239 | elif r0 = self.bounds then println "trivial substring"; self |
| | 240 | else SubString'(parent, r0≫range.lower) |
| | 241 | end |
| | 242 | end |
| | 243 | |
| | 244 | println(self): () = do print(self) ; println() end |
| | 245 | print(self): () = for ch ← seq(self) do print ch end |
| | 246 | |
| | 247 | subdivide(): Maybe⟦Generator⟦(ZZ32, String)⟧⟧ = do |
| | 248 | (* The pairs (start, str) that are generated are such that start[0] = 0 and start[i] = | str[0] || ... || str[i-1] | |
| | 249 | and str[0] || str [1] || ... || str[n] = self |
| | 250 | *) |
| | 251 | pieces = parent.subdivide() |
| | 252 | offset = range.lower |
| | 253 | if pgen ← pieces then |
| | 254 | <| if (start#piece.size) ≤ range then |
| | 255 | (start-offset, piece) |
| | 256 | else |
| | 257 | range' = range ∩ (start#piece.size) |
| | 258 | (range'.lower-offset, SubString'(piece, range'≪start)) |
| | 259 | end | (start, piece) ← pgen, (range ∩ (start#piece.size)).nonEmpty |> |
| | 260 | else |
| | 261 | Nothing⟦Generator⟦(ZZ32,String)⟧⟧ |
| | 262 | end |
| | 263 | end |