1(* 2 Copyright (c) 2009 David C.J. Matthews 2008, 2013, 2015. 3 4 This library is free software; you can redistribute it and/or 5 modify it under the terms of the GNU Lesser General Public 6 License version 2.1 as published by the Free Software Foundation. 7 8 This library is distributed in the hope that it will be useful, 9 but WITHOUT ANY WARRANTY; without even the implied warranty of 10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 Lesser General Public License for more details. 12 13 You should have received a copy of the GNU Lesser General Public 14 License along with this library; if not, write to the Free Software 15 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 16*) 17 18structure Pretty:> PRETTYSIG = 19struct 20 21(* abstype context = 22 AbsContextLocation of 23 { file: string, startLine: int, startPosition: int, endLine: int, endPosition: int } 24 | AbsContextProperty of string * string (* User property. *) 25 26 and pretty = 27 AbsPrettyBlock of int * bool * context list * pretty list 28 | AbsPrettyBreak of int * int 29 | AbsPrettyString of string 30 | AbsPrettyStringAndWidth of string * int 31 | AbsPrettyLineBreak 32 33 with 34 val ContextLocation = AbsContextLocation 35 and ContextProperty = AbsContextProperty 36 37 val PrettyBlock = AbsPrettyBlock 38 and PrettyBreak = AbsPrettyBreak 39 and PrettyString = AbsPrettyString 40 41 fun isPrettyBlock(AbsPrettyBlock _) = true | isPrettyBlock _ = false 42 and isPrettyBreak(AbsPrettyBreak _) = true | isPrettyBreak _ = false 43 and isPrettyString(AbsPrettyString _) = true | isPrettyString _ = false 44 45 fun projPrettyBlock(AbsPrettyBlock b) = b | projPrettyBlock _ = raise Match 46 and projPrettyBreak(AbsPrettyBreak b) = b | projPrettyBreak _ = raise Match 47 and projPrettyString(AbsPrettyString b) = b | projPrettyString _ = raise Match 48 end;*) 49 50 (* This is complicated because the data structures we use here will be exported into 51 the code produced by the compiler. We can't assume that the same representations 52 will be used by this version of the compiler as are used by the compiler that is 53 compiling this code. We use an explicit representation here which must be kept in 54 synch with the representation used in DATATYPE_REP.ML *) 55 local 56 open Address 57 fun cast p = toAddress(toMachineWord p) 58 in 59 type context = address 60 type loc = { file: string, startLine: FixedInt.int, startPosition: FixedInt.int, endLine: FixedInt.int, endPosition: FixedInt.int } 61 (* Because the argument tuple has more than 4 fields the address is used rather than copying the fields. *) 62 fun ContextLocation(p: loc): context = cast(0w0, p) 63 and ContextProperty(s1: string, s2: string): context = cast(0w1, s1, s2) 64 end 65 66 local 67 open Address 68 fun cast p = toAddress(toMachineWord p) 69 in 70 type pretty = address 71 72 val tagPrettyBlock = 0w0 73 and tagPrettyBreak = 0w1 74 (*and tagPrettyLineBreak = 0w2*) (* Not used in the compiler. *) 75 and tagPrettyString = 0w3 76 (*and tagPrettyStringWithWidth = 0w4*) (* Not used in the compiler. *) 77 78 val maxPrettyTag = 0w4 (* Exported because it is used in TagTest. *) 79 80 fun PrettyBlock(offset: FixedInt.int, consistent: bool, context: context list, items: pretty list): pretty = 81 cast(tagPrettyBlock, offset, consistent, context, items) 82 and PrettyBreak(breaks: FixedInt.int, offset: FixedInt.int): pretty = cast(tagPrettyBreak, breaks, offset) 83 and PrettyString(s: string): pretty = cast(tagPrettyString, s) 84 85 fun isPrettyBlock p = toShort(loadWord(p, 0w0)) = tagPrettyBlock 86 and isPrettyBreak p = toShort(loadWord(p, 0w0)) = tagPrettyBreak 87 and isPrettyString p = toShort(loadWord(p, 0w0)) = tagPrettyString 88 89 fun projPrettyBlock p = 90 if isPrettyBlock p 91 then 92 let 93 val (_: int, offset: int, consistent: bool, context: context list, items: pretty list) = 94 RunCall.unsafeCast p 95 in 96 (offset, consistent, context, items) 97 end 98 else raise Match 99 100 and projPrettyBreak p = 101 if isPrettyBreak p 102 then 103 let 104 val (_: int, breaks: int, offset: int) = RunCall.unsafeCast p 105 in 106 (breaks, offset) 107 end 108 else raise Match 109 110 and projPrettyString p = 111 if isPrettyString p 112 then 113 let 114 val (_: int, s: string) = RunCall.unsafeCast p 115 in 116 s 117 end 118 else raise Match 119 end 120 121 fun uglyPrint p = 122 if isPrettyBlock p then String.concat(map uglyPrint(#4 (projPrettyBlock p))) 123 else if isPrettyBreak p then String.implode(List.tabulate(#1 (projPrettyBreak p), fn _ => #" ")) 124 else projPrettyString p 125 126 (* Pretty printer copied directly from basis/PrettyPrinter. We can't use the 127 same code because the "pretty" type is not the same. *) 128 fun prettyPrint (stream : string -> unit, lineWidth : int) (pretty: pretty): unit = 129 let 130 fun printBlanks n = 131 if n > 0 then (stream " "; printBlanks(n-1)) else () 132 133 (* Find out whether the block fits and return the space left if it does. 134 Terminates with NONE as soon as it finds the line doesn't fit. *) 135 fun getSize(p, spaceLeft) = 136 if isPrettyBlock p 137 then 138 let 139 val (_, _, _, entries) = projPrettyBlock p 140 in 141 List.foldl(fn (p, SOME s) => getSize(p, s) | (_, NONE) => NONE) 142 (SOME spaceLeft) entries 143 end 144 145 else if isPrettyBreak p 146 then 147 let 148 val (blanks, _) = projPrettyBreak p 149 in 150 if blanks <= spaceLeft then SOME(spaceLeft-blanks) else NONE 151 end 152 153 else 154 let 155 val size = String.size (projPrettyString p) 156 in 157 if size <= spaceLeft 158 then SOME(spaceLeft-size) 159 else NONE 160 end 161 162 (* Lay out the block and return the space that is left after the line 163 has been printed. *) 164 fun layOut (p, indent, spaceLeft) = 165 if isPrettyBlock p 166 then 167 let 168 val (blockOffset, consistent, _, entries) = projPrettyBlock p 169 val blockIndent = indent+blockOffset 170 in 171 case getSize(p, spaceLeft) of 172 SOME s => (* Fits *) 173 ( 174 (* Lay out the contents. This will not need to break. *) 175 List.foldl(fn(p, space) => layOut(p, blockIndent, space)) spaceLeft entries; 176 s 177 ) 178 | NONE => (* Doesn't fit - break line somewhere. *) 179 let 180 (* Lay out this block, breaking where necessary. *) 181 fun doPrint([], left) = (* Finished: return what's left. *) left 182 183 | doPrint(hd :: rest, left) = 184 if isPrettyBreak hd 185 then if null rest 186 then left (* Ignore trailing breaks. *) 187 else 188 let 189 val (blanks, breakOffset) = projPrettyBreak hd 190 (* Compute the space of the next item(s) up to the end or the 191 next space. Since we only break at spaces if there are 192 Blocks or Strings without spaces between we need to know 193 the total size. *) 194 fun getsp([], left) = SOME left 195 | getsp(next::rest, left) = 196 if isPrettyBreak next 197 then SOME left 198 else case getSize(next, left) of 199 NONE => NONE 200 | SOME sp => getsp(rest, sp) 201 in 202 if consistent orelse left <= blanks orelse 203 not(isSome(getsp(rest, left-blanks))) 204 then (* Either a consistent break or the next item won't fit. *) 205 ( 206 stream "\n"; 207 printBlanks(blockIndent+breakOffset); 208 doPrint(rest, lineWidth-blockIndent-breakOffset) 209 ) 210 else (* We don't need to break here. *) 211 ( 212 printBlanks blanks; 213 doPrint(rest, left-blanks) 214 ) 215 end 216 217 else if isPrettyString hd 218 then 219 let 220 val s = projPrettyString hd 221 in 222 stream s; 223 doPrint(rest, left-size s) 224 end 225 226 else (* Block *) doPrint(rest, layOut(hd, blockIndent, left)) 227 228 val onLine = doPrint(entries, spaceLeft); 229 in 230 onLine 231 end 232 end 233 234 else if isPrettyBreak p 235 then 236 let 237 val (blanks, _) = projPrettyBreak p 238 in 239 printBlanks blanks; Int.max(spaceLeft-blanks, 0) 240 end 241 242 else 243 let 244 val st = projPrettyString p 245 in 246 stream st; Int.max(spaceLeft-String.size st, 0) 247 end 248 249 in 250 if layOut(pretty, 0, lineWidth) <> lineWidth 251 then stream "\n" (* End the line unless we haven't written anything. *) 252 else () 253 end 254 255 local 256 open Universal 257 in 258 (* Tag for pretty printed out from PolyML.print. *) 259 val printOutputTag : (pretty -> unit) tag = tag() 260 (* Compiler output. Used for timing information and compiler debug output. *) 261 and compilerOutputTag: (pretty->unit) tag = tag() 262 end 263 264 local 265 open Universal 266 fun getTag (t: (pretty -> unit) tag) (tagList: universal list) : pretty -> unit = 267 case List.find (tagIs t) tagList of 268 SOME a => tagProject t a 269 | NONE => fn _ => () (* Use the default *) 270 in 271 val getPrintOutput = getTag printOutputTag 272 and getCompilerOutput = getTag compilerOutputTag 273 274 (* The low-level code-generators print strings a bit at a time and separate the lines 275 with new-line characters. This provides a simple string printer for backwards 276 compatibility. It has now been extended with explicit tab stops. By default 277 tabs are inserted every eight characters. *) 278 fun getSimplePrinter(parameters, tabStops) = 279 let 280 val compilerOut: pretty -> unit = getTag compilerOutputTag parameters 281 val buff = ref "" 282 fun printStream (s: string) = 283 let 284 (* If there's a newline split there. *) 285 val (a, b) = Substring.splitl(fn #"\n" => false | _ => true) (Substring.full(!buff ^ s)) 286 in 287 if Substring.size b = 0 (* No newline. *) 288 then buff := Substring.string a 289 else 290 let 291 val str = Substring.string a 292 (* Split at the tabs. *) 293 val fields = String.fields (fn #"\t" => true | _ => false) str 294 fun nSpaces n = if n <= 0 then "" else CharVector.tabulate(n, fn _ => #" ") 295 fun rebuild(f::l, tabs) = 296 let 297 fun findTab(length, []) = 298 (8 - Int.rem(length, 8), []) 299 | findTab(length, tab::tabs) = 300 if length >= tab 301 then 302 let 303 val (t, ts) = findTab(length-tab, tabs) 304 in 305 (t+length, ts) 306 end 307 else (tab-length, tabs) 308 val (spaces, nextTab) = findTab(size f, tabs) 309 in 310 f :: nSpaces spaces :: rebuild(l, nextTab) 311 end 312 313 | rebuild([], _) = [] 314 in 315 compilerOut(PrettyString(String.concat(rebuild(fields, tabStops)))); 316 buff := ""; 317 printStream(Substring.string(Substring.slice(b, 1, NONE))) 318 end 319 end 320 in 321 printStream 322 end 323 end 324 325 (* Types that can be shared. *) 326 structure Sharing = 327 struct 328 type pretty = pretty 329 and context = context 330 end 331end; 332