[project @ 2000-11-17 16:53:27 by simonmar]
[ghc-hetmet.git] / ghc / compiler / utils / StringBuffer.lhs
1 %
2 % (c) The GRASP/AQUA Project, Glasgow University, 1997-1998
3 %
4 \section{String buffers}
5
6 Buffers for scanning string input stored in external arrays.
7
8 \begin{code}
9 {-# OPTIONS -fno-prune-tydecls #-}
10 module StringBuffer
11        (
12         StringBuffer,
13
14          -- creation/destruction
15         hGetStringBuffer,     -- :: FilePath     -> IO StringBuffer
16         stringToStringBuffer, -- :: String       -> IO StringBuffer
17         freeStringBuffer,     -- :: StringBuffer -> IO ()
18
19          -- Lookup
20         currentChar,      -- :: StringBuffer -> Char
21         currentChar#,     -- :: StringBuffer -> Char#
22         indexSBuffer,     -- :: StringBuffer -> Int -> Char
23         indexSBuffer#,    -- :: StringBuffer -> Int# -> Char#
24          -- relative lookup, i.e, currentChar = lookAhead 0
25         lookAhead,        -- :: StringBuffer -> Int  -> Char
26         lookAhead#,       -- :: StringBuffer -> Int# -> Char#
27         
28         -- offsets
29         currentIndex#,    -- :: StringBuffer -> Int#
30         lexemeIndex,      -- :: StringBuffer -> Int#
31
32          -- moving the end point of the current lexeme.
33         setCurrentPos#,   -- :: StringBuffer -> Int# -> StringBuffer
34         incLexeme,        -- :: StringBuffer -> StringBuffer
35         decLexeme,        -- :: StringBuffer -> StringBuffer
36
37          -- move the start and end lexeme pointer on by x units.        
38         stepOn,           -- :: StringBuffer -> StringBuffer
39         stepOnBy#,        -- :: StringBuffer -> Int# -> StringBuffer
40         stepOnTo#,        -- :: StringBuffer -> Int# -> StringBuffer
41         stepOnUntil,      -- :: (Char -> Bool) -> StringBuffer -> StringBuffer
42         stepOnUntilChar#, -- :: StringBuffer -> Char# -> StringBuffer
43         stepOverLexeme,   -- :: StringBuffer   -> StringBuffer
44         scanNumLit,       -- :: Int -> StringBuffer -> (Int, StringBuffer)
45         squeezeLexeme,    -- :: StringBuffer -> Int# -> StringBuffer
46         mergeLexemes,     -- :: StringBuffer -> StringBuffer -> StringBuffer
47         expandWhile,      -- :: (Char  -> Bool) -> StringBuffer -> StringBuffer
48         expandWhile#,     -- :: (Char# -> Bool) -> StringBuffer -> StringBuffer
49         expandUntilMatch, -- :: StrinBuffer -> String -> StringBuffer
50          -- at or beyond end of buffer?
51         bufferExhausted,  -- :: StringBuffer -> Bool
52         emptyLexeme,      -- :: StringBuffer -> Bool
53
54          -- matching
55         prefixMatch,       -- :: StringBuffer -> String -> Bool
56         untilEndOfString#, -- :: StringBuffer -> Int#
57
58          -- conversion
59         lexemeToString,     -- :: StringBuffer -> String
60         lexemeToByteArray,  -- :: StringBuffer -> _ByteArray Int
61         lexemeToFastString, -- :: StringBuffer -> FastString
62         lexemeToBuffer,     -- :: StringBuffer -> StringBuffer
63
64         FastString,
65         ByteArray
66        ) where
67
68 #include "HsVersions.h"
69
70 import GlaExts
71 import PrelAddr         ( Addr(..) )
72 import Foreign
73 import Char             ( chr )
74
75 -- urk!
76 #include "../lib/std/cbits/stgerror.h"
77
78 #if __GLASGOW_HASKELL__ >= 303
79 import IO               ( openFile
80 #if __GLASGOW_HASKELL__ < 407
81                         , slurpFile   -- comes from PrelHandle or IOExts now
82 #endif
83                         )
84 import PrelIOBase
85 import PrelHandle
86 import Addr
87 #else
88 import IO               ( openFile, hFileSize, hClose, IOMode(..) )
89 import Addr
90 #endif
91
92 #if __GLASGOW_HASKELL__ < 301
93 import IOBase           ( Handle, IOError(..), IOErrorType(..),
94                           constructErrorAndFail )
95 import IOHandle         ( readHandle, writeHandle, filePtr )
96 import PackBase         ( unpackCStringBA )
97 #else
98 # if __GLASGOW_HASKELL__ <= 302
99 import PrelIOBase       ( Handle, IOError(..), IOErrorType(..), 
100                           constructErrorAndFail )
101 import PrelHandle       ( readHandle, writeHandle, filePtr )
102 # endif
103 import PrelPack         ( unpackCStringBA )
104 #endif
105
106 #if __GLASGOW_HASKELL__ < 402
107 import Util             ( bracket )
108 #else
109 import Exception        ( bracket )
110 #endif
111
112 import PrimPacked
113 import FastString
114 import Char             (isDigit)
115 \end{code} 
116
117 \begin{code}
118 data StringBuffer
119  = StringBuffer
120      Addr#
121      Int#         -- length
122      Int#         -- lexeme start
123      Int#         -- current pos
124 \end{code}
125
126 \begin{code}
127 instance Show StringBuffer where
128         showsPrec _ s = showString ""
129 \end{code}
130
131 \begin{code}
132 hGetStringBuffer :: Bool -> FilePath -> IO StringBuffer
133 hGetStringBuffer expand_tabs fname = do
134    (a, read) <- if expand_tabs 
135                                 then slurpFileExpandTabs fname 
136                                 else slurpFile fname
137
138    let (A# a#) = a;  (I# read#) = read
139
140          -- add sentinel '\NUL'
141    _casm_ `` ((char *)%0)[(int)%1]=(char)0; '' (A# a#) (I# (read# -# 1#))
142    return (StringBuffer a# read# 0# 0#)
143
144 #if __GLASGOW_HASKELL__ < 303
145 slurpFile fname =
146     openFile fname ReadMode >>= \ hndl ->
147     hFileSize hndl          >>= \ len ->
148     let len_i = fromInteger len in
149       -- Allocate an array for system call to store its bytes into.
150       -- ToDo: make it robust
151 --    trace (show ((len_i::Int)+1)) $
152     _casm_ `` %r=(char *)malloc(sizeof(char)*(int)%0); '' (len_i::Int)  >>= \ arr@(A# a#) ->
153     if addr2Int# a# ==# 0# then
154        fail (userError ("hGetStringBuffer: Could not allocate "++show len_i ++ " bytes"))
155     else
156     readHandle hndl        >>= \ hndl_ ->
157     writeHandle hndl hndl_ >>
158      let ptr = filePtr hndl_ in
159 #if __GLASGOW_HASKELL__ <= 302
160      _ccall_ fread arr (1::Int) len_i (ptr::ForeignObj)               >>= \  (I# read#) ->
161 #else
162      _ccall_ fread arr (1::Int) len_i (ptr::Addr)                     >>= \  (I# read#) ->
163 #endif
164      hClose hndl                     >>
165      if read# ==# 0# then -- EOF or some other error
166         fail (userError ("hGetStringBuffer: failed to slurp in interface file "++fname))
167      else
168         return (arr, I# read#)
169 #endif
170
171 unsafeWriteBuffer :: StringBuffer -> Int# -> Char# -> StringBuffer
172 unsafeWriteBuffer s@(StringBuffer a _ _ _) i# ch# =
173  unsafePerformIO (
174    _casm_ `` ((char *)%0)[(int)%1]=(char)%2; '' (A# a) (I# i#) (C# ch#) >>= \ () ->
175    return s
176  )
177 \end{code}
178
179 -----------------------------------------------------------------------------
180 -- Turn a String into a StringBuffer
181
182 \begin{code}
183 stringToStringBuffer :: String -> IO StringBuffer
184 stringToStringBuffer str =
185   do let sz@(I# sz#) = length str + 1
186      (Ptr a@(A# a#)) <- mallocBytes sz
187      fill_in str a
188      writeCharOffAddr a (sz-1) '\0'             -- sentinel
189      return (StringBuffer a# sz# 0# 0#)
190  where
191   fill_in [] _ = return ()
192   fill_in (c:cs) a = do
193     writeCharOffAddr a 0 c 
194     fill_in cs (a `plusAddr` 1)
195
196 freeStringBuffer :: StringBuffer -> IO ()
197 freeStringBuffer (StringBuffer a# _ _ _) = Foreign.free (Ptr (A# a#))
198 \end{code}
199
200 -----------------------------------------------------------------------------
201 This very disturbing bit of code is used for expanding the tabs in a
202 file before we start parsing it.  Expanding the tabs early makes the
203 lexer a lot simpler: we only have to record the beginning of the line
204 in order to be able to calculate the column offset of the current
205 token.
206
207 We guess the size of the buffer required as 20% extra for
208 expanded tabs, and enlarge it if necessary.
209
210 \begin{code}
211 #if __GLASGOW_HASKELL__ < 303
212 mayBlock fo thing = thing
213
214 writeCharOffAddr :: Addr -> Int -> Char -> IO ()
215 writeCharOffAddr addr off c
216   = _casm_ ``*((char *)%0+(int)%1)=(char)%2;'' addr off c
217 #endif
218
219 getErrType :: IO Int
220 #if __GLASGOW_HASKELL__ < 303
221 getErrType = _casm_ ``%r = ghc_errtype;''
222 #else
223 getErrType =  _ccall_ getErrType__
224 #endif
225
226 slurpFileExpandTabs :: FilePath -> IO (Addr,Int)
227 slurpFileExpandTabs fname = do
228   bracket (openFile fname ReadMode) (hClose) 
229    (\ handle ->
230      do sz <- hFileSize handle
231         if sz > toInteger (maxBound::Int) 
232           then IOERROR (userError "slurpFile: file too big")
233           else do
234             let sz_i = fromInteger sz
235                 sz_i' = (sz_i * 12) `div` 10            -- add 20% for tabs
236             chunk <- allocMem sz_i'
237             trySlurp handle sz_i' chunk
238    )
239
240 trySlurp :: Handle -> Int -> Addr -> IO (Addr, Int)
241 trySlurp handle sz_i chunk =
242 #if __GLASGOW_HASKELL__ == 303
243   wantReadableHandle "hGetChar" handle >>= \ handle_ ->
244   let fo = haFO__ handle_ in
245 #elif __GLASGOW_HASKELL__ > 303
246   wantReadableHandle "hGetChar" handle $ \ handle_ ->
247   let fo = haFO__ handle_ in
248 #else
249   readHandle handle        >>= \ handle_ ->
250   let fo = filePtr handle_ in
251 #endif
252   let
253         (I# chunk_sz) = sz_i
254
255         tAB_SIZE = 8#
256
257         slurpFile :: Int# -> Int# -> Addr -> Int# -> Int# -> IO (Addr, Int)
258         slurpFile c off chunk chunk_sz max_off = slurp c off
259          where
260
261           slurp :: Int# -> Int# -> IO (Addr, Int)
262           slurp c off | off >=# max_off = do
263                 let new_sz = chunk_sz *# 2#
264                 chunk' <- reAllocMem chunk (I# new_sz)
265                 slurpFile c off chunk' new_sz (new_sz -# (tAB_SIZE +# 1#))
266           slurp c off = do
267                 intc <- mayBlock fo (_ccall_ fileGetc fo)
268                 if intc == ((-1)::Int)
269                   then do errtype <- getErrType
270                           if errtype == (ERR_EOF :: Int)
271                             then return (chunk, I# off)
272                             else constructErrorAndFail "slurpFile"
273                   else case chr intc of
274                          '\t' -> tabIt c off
275                          ch   -> do  writeCharOffAddr chunk (I# off) ch
276                                      let c' | ch == '\n' = 0#
277                                             | otherwise  = c +# 1#
278                                      slurp c' (off +# 1#)
279
280           tabIt :: Int# -> Int# -> IO (Addr, Int)
281           -- can't run out of buffer in here, because we reserved an
282           -- extra tAB_SIZE bytes at the end earlier.
283           tabIt c off = do
284                 writeCharOffAddr chunk (I# off) ' '
285                 let c' = c +# 1#
286                     off' = off +# 1#
287                 if c' `remInt#` tAB_SIZE ==# 0#
288                         then slurp c' off'
289                         else tabIt c' off'
290   in do
291
292         -- allow space for a full tab at the end of the buffer
293         -- (that's what the max_off thing is for),
294         -- and add 1 to allow room for the final sentinel \NUL at
295         -- the end of the file.
296   (chunk', rc) <- slurpFile 0# 0# chunk chunk_sz (chunk_sz -# (tAB_SIZE +# 1#))
297 #if __GLASGOW_HASKELL__ < 404
298   writeHandle handle handle_
299 #endif
300   if rc < (0::Int)
301         then constructErrorAndFail "slurpFile"
302         else return (chunk', rc+1 {-room for sentinel-})
303
304
305 reAllocMem :: Addr -> Int -> IO Addr
306 reAllocMem ptr sz = do
307    chunk <- _ccall_ realloc ptr sz
308    if chunk == nullAddr 
309 #if __GLASGOW_HASKELL__ >= 400
310       then fail "reAllocMem"
311 #else
312       then fail (userError "reAllocMem")
313 #endif
314       else return chunk
315
316 allocMem :: Int -> IO Addr
317 allocMem sz = do
318    chunk <- _ccall_ malloc sz
319 #if __GLASGOW_HASKELL__ < 303
320    if chunk == nullAddr 
321       then fail (userError "allocMem")
322       else return chunk
323 #else
324    if chunk == nullAddr 
325       then constructErrorAndFail "allocMem"
326       else return chunk
327 #endif
328 \end{code}
329
330 Lookup
331
332 \begin{code}
333 currentChar  :: StringBuffer -> Char
334 currentChar sb = case currentChar# sb of c -> C# c
335
336 lookAhead :: StringBuffer -> Int  -> Char
337 lookAhead sb (I# i#) = case lookAhead# sb i# of c -> C# c
338
339 indexSBuffer :: StringBuffer -> Int -> Char
340 indexSBuffer sb (I# i#) = case indexSBuffer# sb i# of c -> C# c
341
342 currentChar# :: StringBuffer -> Char#
343 indexSBuffer# :: StringBuffer -> Int# -> Char#
344 lookAhead# :: StringBuffer -> Int# -> Char#
345 currentChar# (StringBuffer fo# _ _ current#) = indexCharOffAddr# fo# current#
346 indexSBuffer# (StringBuffer fo# _ _ _) i# = indexCharOffAddr# fo# i#
347
348  -- relative lookup, i.e, currentChar = lookAhead 0
349 lookAhead# (StringBuffer fo# _ _ c#) i# = indexCharOffAddr# fo# (c# +# i#)
350
351 currentIndex# :: StringBuffer -> Int#
352 currentIndex# (StringBuffer fo# _ _ c#) = c#
353
354 lexemeIndex :: StringBuffer -> Int#
355 lexemeIndex (StringBuffer fo# _ c# _) = c#
356 \end{code}
357
358  moving the start point of the current lexeme.
359
360 \begin{code}
361  -- moving the end point of the current lexeme.
362 setCurrentPos# :: StringBuffer -> Int# -> StringBuffer
363 setCurrentPos# (StringBuffer fo l# s# c#) i# =
364  StringBuffer fo l# s# (c# +# i#)
365
366 -- augmenting the current lexeme by one.
367 incLexeme :: StringBuffer -> StringBuffer
368 incLexeme (StringBuffer fo l# s# c#) = StringBuffer fo l# s# (c# +# 1#)
369
370 decLexeme :: StringBuffer -> StringBuffer
371 decLexeme (StringBuffer fo l# s# c#) = StringBuffer fo l# s# (c# -# 1#)
372
373 \end{code}
374
375 -- move the start and end point of the buffer on by
376 -- x units.        
377
378 \begin{code}
379 stepOn :: StringBuffer -> StringBuffer
380 stepOn (StringBuffer fo l# s# c#) = StringBuffer fo l# (s# +# 1#) (s# +# 1#) -- assume they're the same.
381
382 stepOnBy# :: StringBuffer -> Int# -> StringBuffer
383 stepOnBy# (StringBuffer fo# l# s# c#) i# = 
384  case s# +# i# of
385   new_s# -> StringBuffer fo# l# new_s# new_s#
386
387 -- jump to pos.
388 stepOnTo# :: StringBuffer -> Int# -> StringBuffer
389 stepOnTo# (StringBuffer fo l _ _) s# = StringBuffer fo l s# s#
390
391 squeezeLexeme :: StringBuffer -> Int# -> StringBuffer
392 squeezeLexeme (StringBuffer fo l s# c#) i# = StringBuffer fo l (s# +# i#) c#
393
394 mergeLexemes :: StringBuffer -> StringBuffer -> StringBuffer
395 mergeLexemes (StringBuffer fo l s# _) (StringBuffer _ _ _ c#)
396    = StringBuffer fo l s# c#
397
398 stepOnUntil :: (Char -> Bool) -> StringBuffer -> StringBuffer
399
400 stepOnUntil pred (StringBuffer fo l# s# c#) =
401  loop c#
402   where
403    loop c# = 
404     case indexCharOffAddr# fo c# of
405      ch# | pred (C# ch#) -> StringBuffer fo l# c# c#
406          | ch# `eqChar#` '\NUL'# && c# >=# l# -> StringBuffer fo l# l# l# -- EOB, return immediately.
407          | otherwise     -> loop (c# +# 1#)
408
409 stepOverLexeme :: StringBuffer -> StringBuffer
410 stepOverLexeme (StringBuffer fo l s# c#) = StringBuffer fo l c# c#
411
412 expandWhile :: (Char -> Bool) -> StringBuffer -> StringBuffer
413 expandWhile pred (StringBuffer fo l# s# c#) =
414  loop c#
415   where
416    loop c# = 
417     case indexCharOffAddr# fo c# of
418      ch# | pred (C# ch#) -> loop (c# +# 1#)
419          | ch# `eqChar#` '\NUL'# && c# >=# l# -> StringBuffer fo l# l# l# -- EOB, return immediately.
420          | otherwise     -> StringBuffer fo l# s# c#
421
422 expandWhile# :: (Char# -> Bool) -> StringBuffer -> StringBuffer
423 expandWhile# pred (StringBuffer fo l# s# c#) =
424  loop c#
425   where
426    loop c# = 
427     case indexCharOffAddr# fo c# of
428      ch# | pred ch# -> loop (c# +# 1#)
429          | ch# `eqChar#` '\NUL'# && c# >=# l# -> StringBuffer fo l# s# c# -- EOB, return immediately.
430          | otherwise     -> StringBuffer fo l# s# c#
431
432 scanNumLit :: Integer -> StringBuffer -> (Integer,StringBuffer)
433 scanNumLit acc (StringBuffer fo l# s# c#) =
434  loop acc c#
435   where
436    loop acc c# = 
437     case indexCharOffAddr# fo c# of
438      ch# | isDigit (C# ch#) -> loop (acc*10 + (toInteger (I# (ord# ch# -# ord# '0'#)))) (c# +# 1#)
439          | ch# `eqChar#` '\NUL'# && c# >=# l# -> (acc, StringBuffer fo l# s# c#) -- EOB, return immediately.
440          | otherwise        -> (acc,StringBuffer fo l# s# c#)
441
442
443 expandUntilMatch :: StringBuffer -> String -> Maybe StringBuffer
444 expandUntilMatch (StringBuffer fo l# s# c#) str =
445   loop c# str
446   where
447    loop c# [] = Just (StringBuffer fo l# s# c#)
448    loop c# ((C# x#):xs) =
449     case indexCharOffAddr# fo c# of
450       ch# | ch# `eqChar#` '\NUL'# && c# >=# l# -> Nothing
451           | ch# `eqChar#` x# -> loop (c# +# 1#) xs
452           | otherwise        -> loop (c# +# 1#) str
453         
454 \end{code}
455
456 \begin{code}
457    -- at or beyond end of buffer?
458 bufferExhausted :: StringBuffer -> Bool
459 bufferExhausted (StringBuffer fo l# _ c#) = c# >=# l#
460
461 emptyLexeme :: StringBuffer -> Bool
462 emptyLexeme (StringBuffer fo l# s# c#) = s# ==# c#
463
464  -- matching
465 prefixMatch :: StringBuffer -> String -> Maybe StringBuffer
466 prefixMatch (StringBuffer fo l# s# c#) str =
467   loop c# str
468   where
469    loop c# [] = Just (StringBuffer fo l# s# c#)
470    loop c# ((C# x#):xs)
471      | indexCharOffAddr# fo c# `eqChar#` x#
472      = loop (c# +# 1#) xs
473      | otherwise
474      = Nothing
475
476 untilEndOfString# :: StringBuffer -> StringBuffer
477 untilEndOfString# (StringBuffer fo l# s# c#) = 
478  loop c# 
479  where
480   getch# i# = indexCharOffAddr# fo i#
481
482   loop c# =
483    case getch# c# of
484     '\"'# ->
485       case getch# (c# -# 1#) of
486         '\\'# ->       
487                   -- looks like an escaped something or other to me,
488                   -- better count the number of "\\"s that are immediately
489                   -- preceeding to decide if the " is escaped.
490               let
491                odd_slashes flg i# =
492                 case getch# i# of
493                  '\\'# -> odd_slashes (not flg) (i# -# 1#)
494                  _     -> flg
495               in
496               if odd_slashes True (c# -# 2#) then
497                   -- odd number, " is ecaped.
498                   loop (c# +# 1#)
499               else  -- a real end of string delimiter after all.
500                   StringBuffer fo l# s# c#
501         _ -> StringBuffer fo l# s# c#
502     '\NUL'# ->
503         if c# >=# l# then -- hit sentinel, this doesn't look too good..
504            StringBuffer fo l# l# l#
505         else
506            loop (c# +# 1#)
507     _ -> loop (c# +# 1#)
508
509
510 stepOnUntilChar# :: StringBuffer -> Char# -> StringBuffer
511 stepOnUntilChar# (StringBuffer fo l# s# c#) x# = 
512  loop c# 
513  where
514   loop c#
515    | c# >=# l# || indexCharOffAddr# fo c# `eqChar#` x#
516    = StringBuffer fo l# c# c#
517    | otherwise
518    = loop (c# +# 1#)
519
520          -- conversion
521 lexemeToString :: StringBuffer -> String
522 lexemeToString (StringBuffer fo _ start_pos# current#) = 
523  if start_pos# ==# current# then
524     ""
525  else
526     unpackCStringBA (copySubStr (A# fo) (I# start_pos#) (I# (current# -# start_pos#)))
527     
528 lexemeToByteArray :: StringBuffer -> ByteArray Int
529 lexemeToByteArray (StringBuffer fo _ start_pos# current#) = 
530  if start_pos# ==# current# then
531     error "lexemeToByteArray" 
532  else
533     copySubStr (A# fo) (I# start_pos#) (I# (current# -# start_pos#))
534
535 lexemeToFastString :: StringBuffer -> FastString
536 lexemeToFastString (StringBuffer fo l# start_pos# current#) =
537  if start_pos# ==# current# then
538     mkFastCharString2 (A# fo) (I# 0#)
539  else
540     mkFastSubString (A# fo) (I# start_pos#) (I# (current# -# start_pos#))
541
542 {-
543  Create a StringBuffer from the current lexeme, and add a sentinel
544  at the end. Know What You're Doing before taking this function
545  into use..
546 -}
547 lexemeToBuffer :: StringBuffer -> StringBuffer
548 lexemeToBuffer (StringBuffer fo l# start_pos# current#) =
549  if start_pos# ==# current# then
550     StringBuffer fo 0# start_pos# current# -- an error, really. 
551  else
552     unsafeWriteBuffer (StringBuffer fo (current# -# start_pos#) start_pos# start_pos#)
553                       (current# -# 1#)
554                       '\NUL'#
555
556 \end{code}