{-# LANGUAGE CPP, DeriveDataTypeable #-} ----------------------------------------------------------------------------- -- | -- Module : Language.Python.Common.SrcLocation -- Copyright : (c) 2009 Bernie Pope -- License : BSD-style -- Maintainer : bjpop@csse.unimelb.edu.au -- Stability : experimental -- Portability : ghc -- -- Source location information for the Python lexer and parser. This module -- provides single-point locations and spans, and conversions between them. ----------------------------------------------------------------------------- module Language.JavaScript.Parser.SrcLocation ( -- * Construction AlexPosn (..), AlexSpan, alexStartPos, alexSpanEmpty, SrcLocation (..), SrcSpan (..), Span (..), toSrcSpan, spanning, mkSrcSpan, combineSrcSpans, initialSrcLocation, spanStartPoint, -- * Modification incColumn, decColumn, incLine, incTab, -- endCol, -- * Projection of components of a span -- endRow, -- startCol, -- startRow ) where import Data.Data import Prelude hiding (span) -- | `Posn' records the location of a token in the input text. It has three -- fields: the address (number of characters preceding the token), line number -- and column of a token within the file. `start_pos' gives the position of the -- start of the file and `eof_pos' a standard encoding for the end of file. -- `move_pos' calculates the new position after traversing a given character, -- assuming the usual eight character tab stops. data AlexPosn = AlexPn !Int -- address (number of characters preceding the token) !Int -- line number !Int -- column deriving (Eq,Show) alexStartPos :: AlexPosn alexStartPos = AlexPn 0 1 1 -- AZ bringing this in as SrcSpan replacement. type AlexSpan = (AlexPosn, Char, String) alexSpanEmpty :: AlexSpan alexSpanEmpty = (alexStartPos, '\n', "") -- | A location for a syntactic entity from the source code. -- The location is specified by its filename, and starting row -- and column. data SrcLocation = Sloc { sloc_filename :: !String , sloc_address :: {-# UNPACK #-} !Int -- address (number of characters preceding the token) , sloc_row :: {-# UNPACK #-} !Int , sloc_column :: {-# UNPACK #-} !Int } | NoLocation deriving (Eq,Ord,Show,Typeable,Data) {- instance Pretty SrcLocation where pretty = pretty . getSpan -} -- | Types which have a span. class Span a where getSpan :: a -> SrcSpan getSpan _x = SpanEmpty -- | Create a new span which encloses two spanned things. spanning :: (Span a, Span b) => a -> b -> SrcSpan spanning x y = combineSrcSpans (getSpan x) (getSpan y) instance Span a => Span [a] where getSpan [] = SpanEmpty getSpan [x] = getSpan x getSpan list@(x:_xs) = combineSrcSpans (getSpan x) (getSpan (last list)) instance Span a => Span (Maybe a) where getSpan Nothing = SpanEmpty getSpan (Just x) = getSpan x instance (Span a, Span b) => Span (Either a b) where getSpan (Left x) = getSpan x getSpan (Right x) = getSpan x instance (Span a, Span b) => Span (a, b) where getSpan (x,y) = spanning x y instance Span SrcSpan where getSpan = id -- ++AZ++ adding this instance Span AlexPosn where getSpan ap = toSrcSpan (ap,'\n',"") toSrcSpan :: AlexSpan -> SrcSpan toSrcSpan ((AlexPn _addr line col),_,_) = SpanPoint { span_filename = "", span_row = line, span_column = col} -- ++AZ++ end -- | Construct the initial source location for a file. initialSrcLocation :: String -> SrcLocation initialSrcLocation filename = Sloc { sloc_filename = filename , sloc_address = 1 , sloc_row = 1 , sloc_column = 1 } -- | Decrement the column of a location, only if they are on the same row. decColumn :: Int -> SrcLocation -> SrcLocation decColumn n loc | n < col = loc { sloc_column = col - n } | otherwise = loc where col = sloc_column loc -- | Increment the column of a location. incColumn :: Int -> SrcLocation -> SrcLocation incColumn n loc@(Sloc { sloc_column = col }) = loc { sloc_column = col + n } incColumn _ NoLocation = NoLocation -- | Increment the column of a location by one tab stop. incTab :: SrcLocation -> SrcLocation incTab loc@(Sloc { sloc_column = col }) = loc { sloc_column = newCol } where newCol = col + 8 - (col - 1) `mod` 8 incTab NoLocation = NoLocation -- | Increment the line number (row) of a location by one. incLine :: Int -> SrcLocation -> SrcLocation incLine n loc@(Sloc { sloc_row = row }) = loc { sloc_column = 1, sloc_row = row + n } incLine _ NoLocation = NoLocation {- Inspired heavily by compiler/basicTypes/SrcLoc.lhs A SrcSpan delimits a portion of a text file. -} -- | Source location spanning a contiguous section of a file. data SrcSpan -- | A span which starts and ends on the same line. = SpanCoLinear { span_filename :: !String , span_row :: {-# UNPACK #-} !Int , span_start_column :: {-# UNPACK #-} !Int , span_end_column :: {-# UNPACK #-} !Int } -- | A span which starts and ends on different lines. | SpanMultiLine { span_filename :: !String , span_start_row :: {-# UNPACK #-} !Int , span_start_column :: {-# UNPACK #-} !Int , span_end_row :: {-# UNPACK #-} !Int , span_end_column :: {-# UNPACK #-} !Int } -- | A span which is actually just one point in the file. | SpanPoint { span_filename :: !String , span_row :: {-# UNPACK #-} !Int , span_column :: {-# UNPACK #-} !Int } -- | No span information. | SpanEmpty deriving (Eq,Ord,Show,Read,Typeable,Data) {- instance Show SrcSpan where show (s@(SpanCoLinear filename row start end)) = -- showChar '('.showString filename.shows row.shows start.shows end.showChar ')' -- ("foo.txt" 12 4 5) showChar '(' . showChar ')' -- ("foo.txt" 12 4 5) show (s@(SpanMultiLine filename sr sc er ec)) = showChar '('.showString filename.shows sr.shows sc.shows er.shows ec.showChar ')' -- ("foo.txt" 12 4 13 5) show (s@(SpanPoint filename r c)) = showChar '('.showString filename.shows r.shows c.showChar ')' -- ("foo.txt" 12 4) show (SpanEmpty) = showString "()" -- () -} --instance Read SrcSpan where -- readsPrec _ str = [ {- instance Read a => Read Tree a where readsPrec _ str = [(Leave x, t’) | ("Leave", t) <- reads str, (x, t’) <- reads t] ++ [((Node i r d), t’’’) | ("Node", t) <- reads str, (i, t’) <- reads t, (r, t’’) <- reads t’, (d, t’’’) <- reads t’’] -} instance Span SrcLocation where getSpan loc@(Sloc {}) = SpanPoint { span_filename = sloc_filename loc , span_row = sloc_row loc , span_column = sloc_column loc } getSpan NoLocation = SpanEmpty -- | Make a point span from the start of a span spanStartPoint :: SrcSpan -> SrcSpan spanStartPoint SpanEmpty = SpanEmpty spanStartPoint span = SpanPoint { span_filename = span_filename span , span_row = startRow span , span_column = startCol span } -- | Make a span from two locations. Assumption: either the -- arguments are the same, or the left one preceeds the right one. mkSrcSpan :: SrcLocation -> SrcLocation -> SrcSpan mkSrcSpan NoLocation _ = SpanEmpty mkSrcSpan _ NoLocation = SpanEmpty mkSrcSpan loc1 loc2 | line1 == line2 = if col2 <= col1 then SpanPoint file line1 col1 else SpanCoLinear file line1 col1 col2 | otherwise = SpanMultiLine file line1 col1 line2 col2 where line1 = sloc_row loc1 line2 = sloc_row loc2 col1 = sloc_column loc1 col2 = sloc_column loc2 file = sloc_filename loc1 -- | Combines two 'SrcSpan' into one that spans at least all the characters -- within both spans. Assumes the "file" part is the same in both inputs combineSrcSpans :: SrcSpan -> SrcSpan -> SrcSpan combineSrcSpans SpanEmpty r = r -- this seems more useful combineSrcSpans l SpanEmpty = l combineSrcSpans start end = case row1 `compare` row2 of EQ -> case col1 `compare` col2 of EQ -> SpanPoint file row1 col1 LT -> SpanCoLinear file row1 col1 col2 GT -> SpanCoLinear file row1 col2 col1 LT -> SpanMultiLine file row1 col1 row2 col2 GT -> SpanMultiLine file row2 col2 row1 col1 where row1 = startRow start col1 = startCol start row2 = endRow end col2 = endCol end file = span_filename start -- | Get the row of the start of a span. startRow :: SrcSpan -> Int startRow (SpanCoLinear { span_row = row }) = row startRow (SpanMultiLine { span_start_row = row }) = row startRow (SpanPoint { span_row = row }) = row startRow SpanEmpty = error "startRow called on empty span" -- | Get the row of the end of a span. endRow :: SrcSpan -> Int endRow (SpanCoLinear { span_row = row }) = row endRow (SpanMultiLine { span_end_row = row }) = row endRow (SpanPoint { span_row = row }) = row endRow SpanEmpty = error "endRow called on empty span" -- | Get the column of the start of a span. startCol :: SrcSpan -> Int startCol (SpanCoLinear { span_start_column = col }) = col startCol (SpanMultiLine { span_start_column = col }) = col startCol (SpanPoint { span_column = col }) = col startCol SpanEmpty = error "startCol called on empty span" -- | Get the column of the end of a span. endCol :: SrcSpan -> Int endCol (SpanCoLinear { span_end_column = col }) = col endCol (SpanMultiLine { span_end_column = col }) = col endCol (SpanPoint { span_column = col }) = col endCol SpanEmpty = error "endCol called on empty span"