packages feed

sourcemap (empty) → 0.1.0.0

raw patch · 6 files changed

+307/−0 lines, 6 filesdep +aesondep +attoparsecdep +basesetup-changed

Dependencies added: aeson, attoparsec, base, bytestring, process, unordered-containers, utf8-string

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2012, Chris Done++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Chris Done nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ sourcemap.cabal view
@@ -0,0 +1,27 @@+name:                sourcemap+version:             0.1.0.0+synopsis:            Implementation of source maps as proposed by Google and Mozilla.+description:         Implementation of source maps, revision 3, proposed by Google and Mozilla here+                     https://wiki.mozilla.org/DevTools/Features/SourceMap and here+                     https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit+license:             BSD3+license-file:        LICENSE+author:              Chris Done+stability:           alpha+maintainer:          chrisdone@gmail.com+copyright:           2012 Chris Done+category:            Development+build-type:          Simple+cabal-version:       >=1.8++library+  exposed-modules:     SourceMap, SourceMap.Types+  other-modules:       VLQ+  hs-source-dirs:      src+  build-depends:       base ==4.5.*,+                       bytestring >= 0.10.2.0,+                       aeson,+                       unordered-containers,+                       attoparsec,+                       process,+                       utf8-string
+ src/SourceMap.hs view
@@ -0,0 +1,94 @@+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS -Wall #-}++module SourceMap where++import           SourceMap.Types+import qualified VLQ as VLQ++import           Control.Monad hiding (forM_)+import           Control.Monad.ST+import           Data.Aeson hiding ((.=))+import           Data.ByteString.Lazy (ByteString)+import qualified Data.ByteString.Lazy as Bytes+import           Data.Foldable (forM_)+import qualified Data.HashMap.Lazy as Map+import           Data.List+import           Data.Maybe+import           Data.Ord+import           Data.STRef++-- | Generate the JSON.+generate :: SourceMapping -> Value+generate SourceMapping{..} = Object (Map.fromList obj) where+  obj = [("version",toJSON version)+        ,("file",toJSON smFile)+        ,("sources",toJSON sources)+        ,("names",toJSON names)+        ,("mappings",toJSON (encodeMappings sources names smMappings))] +++        [("sourceRoot",toJSON root) | Just root <- [smSourceRoot]]+  names = nub $ mapMaybe mapName smMappings+  sources = symbols mapSourceFile+  symbols f = sort (nub (mapMaybe f smMappings))++encodeMappings :: [FilePath] -> [String] -> [Mapping] -> ByteString+encodeMappings sources names = go . sortBy (comparing mapGenerated) where+  go mappings = runST $ do+    -- State.+    prevGenCol   <- newSTRef 0+    prevGenLine  <- newSTRef 1+    prevOrigCol  <- newSTRef 0+    prevOrigLine <- newSTRef 0+    prevName     <- newSTRef 0+    prevSource   <- newSTRef 0+    result       <- newSTRef Bytes.empty+    -- Generate the groupings.+    forM_ (zip [0::Integer ..] mappings) $ \(i,Mapping{..}) -> do+      -- Continuations on the same line are separated by “,”, whereas+      -- new lines are separted by “;”.+      updating prevGenLine $ \previousGeneratedLine ->+        if posLine mapGenerated /= previousGeneratedLine+           then do prevGenCol .= 0+                   result += Bytes.replicate (fromIntegral (posLine mapGenerated - previousGeneratedLine))+                                             (fromIntegral (fromEnum ';'))+                   return (posLine mapGenerated)+           else do when (i > 0)+                        (result += ",")+                   return previousGeneratedLine+      -- Original generated column (also offsetted from previous entries).+      updating prevGenCol $ \previousGeneratedColumn -> do+        result += VLQ.encode (posColumn mapGenerated - previousGeneratedColumn)+        return (posColumn mapGenerated)+      -- Optional additional fields.+      case liftM2 (,) mapSourceFile mapOriginal of+        Nothing -> return ()+        Just (source,original) -> do+          -- Source index.+          updating prevSource $ \previousSource -> do+           result += VLQ.encode (indexOf source sources - previousSource)+           return (indexOf source sources)+          -- Original line (also offsetted from previous entries).+          updating prevOrigLine $ \previousOriginalLine -> do+           result += VLQ.encode (posLine original - 1 - previousOriginalLine)+           return (posLine original - 1)+          -- Original column (also offsetted from previous entries).+          updating prevOrigCol $ \previousOriginalColumn -> do+           result += VLQ.encode (posColumn original - previousOriginalColumn)+           return (posColumn original)+          -- Optional name+          forM_ mapName $ \name -> do+            updating prevName $ \previousName -> do+             result += VLQ.encode (indexOf name names - previousName)+             return (indexOf name names)+    -- Return the byte buffer.+    readSTRef result++  updating r f = readSTRef r >>= \x -> f x >>= writeSTRef r+  r += y = modifySTRef r (\x -> Bytes.append x y)+  x .= y = writeSTRef x y; infixr 1 .=+  indexOf e xs = fromIntegral (fromMaybe 0 (elemIndex e xs))++-- | Format version.+version :: Integer+version = 3
+ src/SourceMap/Types.hs view
@@ -0,0 +1,33 @@+{-# OPTIONS -Wall #-}++-- | Types for the source maps.++module SourceMap.Types where++import Data.Int+import Data.Monoid+import Data.Function++-- | The source mapping.+data SourceMapping = SourceMapping+  { smFile       :: FilePath+  , smSourceRoot :: Maybe FilePath+  , smMappings   :: [Mapping]+  } deriving Show++-- | A mapping.+data Mapping = Mapping+  { mapGenerated  :: Pos+  , mapOriginal   :: Maybe Pos+  , mapSourceFile :: Maybe FilePath+  , mapName       :: Maybe String+  } deriving Show++-- | A source position.+data Pos = Pos+  { posLine   :: Int32+  , posColumn :: Int32+  } deriving (Eq,Show)++instance Ord Pos where+  compare a b = on compare posLine a b <> on compare posColumn a b
+ src/VLQ.hs view
@@ -0,0 +1,121 @@+-- | Implements Base 64-encoded VLQ for 32-bit+-- integers. Implementation copied from+-- https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java+--++{-# OPTIONS -Wall #-}++module VLQ+  (encode+  ,decode)+  where++import           Data.Bits hiding (shift)+import           Data.ByteString.Lazy (ByteString)+import qualified Data.ByteString.Lazy as B+import           Data.Int+import           Data.List+import           Data.Word+import           Prelude hiding ((>>))++-- | A Base64 VLQ digit can represent 5 bits, so it is base-32.+baseShift :: Int+baseShift = 5++-- | Base point.+base :: Int32+base = 1 << baseShift++-- | A mask of bits for a VLQ digit (11111), 31 decimal.+baseMask :: Int32+baseMask = base - 1++-- | The continuation bit is the 6th bit.+continuationBit :: Int32+continuationBit = base++-- | Converts from a two-complement value to a value where the sign+-- bit is is placed in the least significant bit.  For example, as+-- decimals:+--   1 becomes 2 (10 binary), -1 becomes 3 (11 binary)+--   2 becomes 4 (100 binary), -2 becomes 5 (101 binary)+toVlqSigned :: Int32 -> Int32+toVlqSigned value =+  if value < 0+     then ((-value) << 1) + 1+     else (value << 1) + 0++-- | Converts to a two-complement value from a value where the sign+-- bit is is placed in the least significant bit.  For example, as+-- decimals:+--   2 (10 binary) becomes 1, 3 (11 binary) becomes -1+--   4 (100 binary) becomes 2, 5 (101 binary) becomes -2+fromVlgSigned :: Int32 -> Int32+fromVlgSigned value =+  let value' = value >> 1+  in if (value & 1) == 1+        then -value'+        else value'++-- | Produces a ByteString containing a VLQ-encoded value of the given 32-bit integer.+encode :: Int32 -> ByteString+encode = B.map encodeBase64 . start where+  start 0 = B.singleton (fst (continue 0))+  start n = B.unfoldr go . toVlqSigned $ n++  go value+    | value <= 0 = Nothing+    | otherwise  = Just (continue value)++  continue value =+    let digit = value & baseMask+        value' = value >> baseShift+        digit' = if value' > 0+                    then digit .|. continuationBit+                    else digit+    in (fromIntegral digit',value')+++-- | Decodes the given VLQ-encoded value into a 32-bit integer.+decode :: ByteString -> Int32+decode = fromVlgSigned . go (0,0) . B.map decodeBase64 where+  go (result,shift) bytes =+    case B.uncons bytes of+      Nothing -> result+      Just (c,next) ->+        let digit = fromIntegral c+            continuation = (digit & continuationBit) /= 0+            digit' = digit & baseMask+            result' = result + (digit' << shift)+            shift' = shift + baseShift+        in if continuation+              then go (result',shift') next+              else result'++-- | Base 64 characters.+base64Chars :: [Word8]+base64Chars = map (fromIntegral.fromEnum) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"++-- | Encode the given number to a base 64 character.+encodeBase64 :: Word8 -> Word8+encodeBase64 i = maybe (error "Base 64 char must be between 0 and 63.")+                       id+                       (lookup i (zip [0..] base64Chars))++-- | Encode the given base 64 character to a number.+decodeBase64 :: Word8 -> Word8+decodeBase64 i = maybe (error "Not a valid base 65 digit.")+                       id+                        (lookup i (zip base64Chars [0..]))++-- | Makes the code more familiar to read. Shift-left.+(<<) :: Int32 -> Int -> Int32+(<<) = shiftL++-- | Makes the code more familiar to read. Shift-right.+(>>) :: Int32 -> Int -> Int32+(>>) = shiftR++-- | Makes the code more familiar to read. And.+(&) :: Int32 -> Int32 -> Int32+(&) = (.&.)