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 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
|
(*
Copyright (c) 2009 David C.J. Matthews 2008, 2013, 2015.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 2.1 as published by the Free Software Foundation.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*)
structure Pretty:> PRETTYSIG =
struct
(* abstype context =
AbsContextLocation of
{ file: string, startLine: int, startPosition: int, endLine: int, endPosition: int }
| AbsContextProperty of string * string (* User property. *)
and pretty =
AbsPrettyBlock of int * bool * context list * pretty list
| AbsPrettyBreak of int * int
| AbsPrettyString of string
| AbsPrettyStringAndWidth of string * int
| AbsPrettyLineBreak
with
val ContextLocation = AbsContextLocation
and ContextProperty = AbsContextProperty
val PrettyBlock = AbsPrettyBlock
and PrettyBreak = AbsPrettyBreak
and PrettyString = AbsPrettyString
fun isPrettyBlock(AbsPrettyBlock _) = true | isPrettyBlock _ = false
and isPrettyBreak(AbsPrettyBreak _) = true | isPrettyBreak _ = false
and isPrettyString(AbsPrettyString _) = true | isPrettyString _ = false
fun projPrettyBlock(AbsPrettyBlock b) = b | projPrettyBlock _ = raise Match
and projPrettyBreak(AbsPrettyBreak b) = b | projPrettyBreak _ = raise Match
and projPrettyString(AbsPrettyString b) = b | projPrettyString _ = raise Match
end;*)
(* This is complicated because the data structures we use here will be exported into
the code produced by the compiler. We can't assume that the same representations
will be used by this version of the compiler as are used by the compiler that is
compiling this code. We use an explicit representation here which must be kept in
synch with the representation used in DATATYPE_REP.ML *)
local
open Address
fun cast p = toAddress(toMachineWord p)
in
type context = address
type loc = { file: string, startLine: int, startPosition: int, endLine: int, endPosition: int }
(* Because the argument tuple has more than 4 fields the address is used rather than copying the fields. *)
fun ContextLocation(p: loc): context = cast(0w0, p)
and ContextProperty(s1: string, s2: string): context = cast(0w1, s1, s2)
end
local
open Address
fun cast p = toAddress(toMachineWord p)
in
type pretty = address
val tagPrettyBlock = 0w0
and tagPrettyBreak = 0w1
(*and tagPrettyLineBreak = 0w2*) (* Not used in the compiler. *)
and tagPrettyString = 0w3
(*and tagPrettyStringWithWidth = 0w4*) (* Not used in the compiler. *)
val maxPrettyTag = 0w4 (* Exported because it is used in TagTest. *)
fun PrettyBlock(offset: int, consistent: bool, context: context list, items: pretty list): pretty =
cast(tagPrettyBlock, offset, consistent, context, items)
and PrettyBreak(breaks: int, offset: int): pretty = cast(tagPrettyBreak, breaks, offset)
and PrettyString(s: string): pretty = cast(tagPrettyString, s)
fun isPrettyBlock p = toShort(loadWord(p, 0w0)) = tagPrettyBlock
and isPrettyBreak p = toShort(loadWord(p, 0w0)) = tagPrettyBreak
and isPrettyString p = toShort(loadWord(p, 0w0)) = tagPrettyString
fun projPrettyBlock p =
if isPrettyBlock p
then
let
val (_: int, offset: int, consistent: bool, context: context list, items: pretty list) =
RunCall.unsafeCast p
in
(offset, consistent, context, items)
end
else raise Match
and projPrettyBreak p =
if isPrettyBreak p
then
let
val (_: int, breaks: int, offset: int) = RunCall.unsafeCast p
in
(breaks, offset)
end
else raise Match
and projPrettyString p =
if isPrettyString p
then
let
val (_: int, s: string) = RunCall.unsafeCast p
in
s
end
else raise Match
end
fun uglyPrint p =
if isPrettyBlock p then String.concat(map uglyPrint(#4 (projPrettyBlock p)))
else if isPrettyBreak p then String.implode(List.tabulate(#1 (projPrettyBreak p), fn _ => #" "))
else projPrettyString p
(* Pretty printer copied directly from basis/PrettyPrinter. We can't use the
same code because the "pretty" type is not the same. *)
fun prettyPrint (stream : string -> unit, lineWidth : int) (pretty: pretty): unit =
let
fun printBlanks n =
if n > 0 then (stream " "; printBlanks(n-1)) else ()
(* Find out whether the block fits and return the space left if it does.
Terminates with NONE as soon as it finds the line doesn't fit. *)
fun getSize(p, spaceLeft) =
if isPrettyBlock p
then
let
val (_, _, _, entries) = projPrettyBlock p
in
List.foldl(fn (p, SOME s) => getSize(p, s) | (_, NONE) => NONE)
(SOME spaceLeft) entries
end
else if isPrettyBreak p
then
let
val (blanks, _) = projPrettyBreak p
in
if blanks <= spaceLeft then SOME(spaceLeft-blanks) else NONE
end
else
let
val size = String.size (projPrettyString p)
in
if size <= spaceLeft
then SOME(spaceLeft-size)
else NONE
end
(* Lay out the block and return the space that is left after the line
has been printed. *)
fun layOut (p, indent, spaceLeft) =
if isPrettyBlock p
then
let
val (blockOffset, consistent, _, entries) = projPrettyBlock p
val blockIndent = indent+blockOffset
in
case getSize(p, spaceLeft) of
SOME s => (* Fits *)
(
(* Lay out the contents. This will not need to break. *)
List.foldl(fn(p, space) => layOut(p, blockIndent, space)) spaceLeft entries;
s
)
| NONE => (* Doesn't fit - break line somewhere. *)
let
(* Lay out this block, breaking where necessary. *)
fun doPrint([], left) = (* Finished: return what's left. *) left
| doPrint(hd :: rest, left) =
if isPrettyBreak hd
then if null rest
then left (* Ignore trailing breaks. *)
else
let
val (blanks, breakOffset) = projPrettyBreak hd
(* Compute the space of the next item(s) up to the end or the
next space. Since we only break at spaces if there are
Blocks or Strings without spaces between we need to know
the total size. *)
fun getsp([], left) = SOME left
| getsp(next::rest, left) =
if isPrettyBreak next
then SOME left
else case getSize(next, left) of
NONE => NONE
| SOME sp => getsp(rest, sp)
in
if consistent orelse left <= blanks orelse
not(isSome(getsp(rest, left-blanks)))
then (* Either a consistent break or the next item won't fit. *)
(
stream "\n";
printBlanks(blockIndent+breakOffset);
doPrint(rest, lineWidth-blockIndent-breakOffset)
)
else (* We don't need to break here. *)
(
printBlanks blanks;
doPrint(rest, left-blanks)
)
end
else if isPrettyString hd
then
let
val s = projPrettyString hd
in
stream s;
doPrint(rest, left-size s)
end
else (* Block *) doPrint(rest, layOut(hd, blockIndent, left))
val onLine = doPrint(entries, spaceLeft);
in
onLine
end
end
else if isPrettyBreak p
then
let
val (blanks, _) = projPrettyBreak p
in
printBlanks blanks; Int.max(spaceLeft-blanks, 0)
end
else
let
val st = projPrettyString p
in
stream st; Int.max(spaceLeft-String.size st, 0)
end
in
if layOut(pretty, 0, lineWidth) <> lineWidth
then stream "\n" (* End the line unless we haven't written anything. *)
else ()
end
local
open Universal
in
(* Tag for pretty printed out from PolyML.print. *)
val printOutputTag : (pretty -> unit) tag = tag()
(* Compiler output. Used for timing information and compiler debug output. *)
and compilerOutputTag: (pretty->unit) tag = tag()
end
local
open Universal
fun getTag (t: (pretty -> unit) tag) (tagList: universal list) : pretty -> unit =
case List.find (tagIs t) tagList of
SOME a => tagProject t a
| NONE => fn _ => () (* Use the default *)
in
val getPrintOutput = getTag printOutputTag
and getCompilerOutput = getTag compilerOutputTag
(* The low-level code-generators print strings a bit at a time and separate the lines
with new-line characters. This provides a simple string printer for backwards
compatibility. *)
fun getSimplePrinter parameters =
let
val compilerOut: pretty -> unit = getTag compilerOutputTag parameters
val buff = ref ""
fun printStream (s: string) =
let
(* If there's a newline split there. *)
val (a, b) = Substring.splitl(fn #"\n" => false | _ => true) (Substring.full(!buff ^ s))
in
if Substring.size b = 0 (* No newline. *)
then buff := Substring.string a
else
(
compilerOut(PrettyString(Substring.string a));
buff := "";
printStream(Substring.string(Substring.slice(b, 1, NONE)))
)
end
in
printStream
end
end
(* Types that can be shared. *)
structure Sharing =
struct
type pretty = pretty
and context = context
end
end;
|