@@ -1,62 +1,225 @@
1{-# OPTIONS_GHC -Wall #-}
1{-# OPTIONS_GHC -Wall #-}
2−{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE EmptyDataDecls, OverloadedStrings, UnboxedTuples #-}
3module Elm.Kernel
3module Elm.Kernel
4− ( Opt.KContent(..)
4+ ( Content(..)
10−import qualified Data.ByteString as B
12+import Control.Monad (liftM, liftM2)
13+import Data.Binary (Binary, get, put, getWord8, putWord8)
14+import Data.Coerce (coerce)
11import qualified Data.List as List
15import qualified Data.List as List
12import qualified Data.Map as Map
16import qualified Data.Map as Map
13import qualified Data.Name as Name
17import qualified Data.Name as Name
14import qualified Data.Set as Set
18import qualified Data.Set as Set
19+import qualified Data.Utf8 as Utf8
15−import Data.Word (Word8)
20+import Data.Word (Word8, Word16)
21+import Foreign.Ptr (Ptr, plusPtr)
18−import qualified AST.Optimized as Opt
19import qualified AST.Source as Src
23import qualified AST.Source as Src
20import qualified Elm.ModuleName as ModuleName
24import qualified Elm.ModuleName as ModuleName
21import qualified Elm.Package as Pkg
25import qualified Elm.Package as Pkg
22−import Parse.Primitives (Parser, run)
23−import qualified Parse.Primitives.Kernel as K
24−import qualified Parse.Primitives.Symbol as Symbol
25import qualified Parse.Module as Module
26import qualified Parse.Module as Module
27+import qualified Parse.Variable as Var
29+import Parse.Primitives hiding (Parser, State)
30+import qualified Parse.Primitives as P
26import qualified Reporting.Annotation as A
31import qualified Reporting.Annotation as A
27−import qualified Reporting.Error as E
32+import qualified Reporting.Error.Syntax as E
34−type ImportDict =
39+data Content =
35− Map.Map Name.Name [Pkg.Name]
40+ Content [Chunk] (Set.Set Dep)
44+ = ElmDep ModuleName.Canonical Name.Name
45+ | KernelDep Name.Name
38−parse :: ImportDict -> B.ByteString -> Either E.Error Opt.KContent
53+data Chunk
39−parse importDict sourceCode =
54+ = JS (Utf8.VeryLong UNCHECKED)
40− case run (parser importDict) sourceCode of
55+ | ElmVar ModuleName.Canonical Name.Name
41− Right kernel ->
56+ | JsVar Name.Name Name.Name
42− Right kernel
57+ | ElmField Name.Name
72+ Map.Map Name.Name [Pkg.Name]
48−parser :: ImportDict -> Parser Opt.KContent
75+parser :: ImportDict -> Parser Content
49parser importDict =
76parser importDict =
50− do Symbol.jsMultiCommentOpen
77+ do word2 0x2F 0x2A {-/*-} E.XXX
51 Module.freshLine
78 Module.freshLine
52 imports <- Module.chompImports []
79 imports <- Module.chompImports []
53− Symbol.jsMultiCommentClose
80+ word2 0x2A 0x2F {-*/-} E.XXX
81+ let (vtable, deps) = processImports importDict imports
82+ chunks <- parseChunks vtable Map.empty Map.empty
83+ return $ Content chunks deps
90+parseChunks :: VarTable -> Enums -> Fields -> Parser [Chunk]
91+parseChunks vtable enums fields =
92+ P.Parser $ \(P.State pos end indent row col ctx) cok _ cerr _ ->
94+ (# chunks, newPos, newRow, newCol #) =
95+ chompChunks vtable enums fields pos end row col pos []
97+ if newPos == end then
98+ cok chunks (P.State newPos end indent newRow newCol ctx)
100+ cerr row col ctx E.XXX
55− let (State vtable deps) = processImports importDict imports
103+addJS :: Ptr Word8 -> Ptr Word8 -> [Chunk] -> [Chunk]
104+addJS start end revChunks =
107+ else JS (Utf8.fromPtr start end) : revChunks
57− chunks <- chompChunks vtable Map.empty Map.empty []
59− return $ Opt.KContent chunks deps
110+chompChunks :: VarTable -> Enums -> Fields -> Ptr Word8 -> Ptr Word8 -> Word16 -> Word16 -> Ptr Word8 -> [Chunk] -> (# [Chunk], Ptr Word8, Word16, Word16 #)
111+chompChunks vs es fs pos end row col lastPos revChunks =
113+ (# reverse (addJS lastPos end revChunks), pos, row, col #)
116+ let !word = unsafeIndex pos in
117+ if word == 0x5F {-_-} then
119+ !pos1 = plusPtr pos 1
120+ !pos3 = plusPtr pos 3
122+ if pos3 <= end && unsafeIndex pos1 == 0x5F {-_-} then
123+ chompTag vs es fs pos3 end row (col + 3) (addJS lastPos pos revChunks)
125+ chompChunks vs es fs pos1 end row (col + 1) lastPos revChunks
127+ else if word == 0x0A {-\n-} then
128+ chompChunks vs es fs (plusPtr pos 1) end (row + 1) 1 lastPos revChunks
132+ !newPos = plusPtr pos (getCharWidth pos end word)
134+ chompChunks vs es fs newPos end row (col + 1) lastPos revChunks
137+-- relies on external checks in chompChunks
138+chompTag :: VarTable -> Enums -> Fields -> Ptr Word8 -> Ptr Word8 -> Word16 -> Word16 -> [Chunk] -> (# [Chunk], Ptr Word8, Word16, Word16 #)
139+chompTag vs es fs pos end row col revChunks =
141+ (# newPos, newCol #) = Var.chompInnerChars pos end col
142+ !tagPos = plusPtr pos (-1)
143+ !word = unsafeIndex tagPos
145+ if word == 0x24 {-$-} then
146+ chompChunks vs es fs newPos end row newCol newPos $
147+ ElmField (Name.fromPtr pos newPos) : revChunks
150+ !name = Name.fromPtr tagPos newPos
152+ if 0x30 {-0-} <= word && word <= 0x39 {-9-} then
155+ lookupEnum (word - 0x30) name es
157+ chompChunks vs newEnums fs newPos end row newCol newPos $
158+ JsEnum enum : revChunks
160+ else if 0x61 {-a-} <= word && word <= 0x7A {-z-} then
162+ (field, newFields) =
165+ chompChunks vs es newFields newPos end row newCol newPos $
166+ JsField field : revChunks
168+ else if name == "DEBUG" then
169+ chompChunks vs es fs newPos end row newCol newPos (Debug : revChunks)
171+ else if name == "PROD" then
172+ chompChunks vs es fs newPos end row newCol newPos (Prod : revChunks)
175+ case Map.lookup name vs of
177+ chompChunks vs es fs newPos end row newCol newPos (chunk : revChunks)
180+ (# revChunks, pos, row, col #)
188+ Map.Map Name.Name Int
191+lookupField :: Name.Name -> Fields -> (Int, Fields)
192+lookupField name fields =
193+ case Map.lookup name fields of
198+ let n = Map.size fields in
199+ ( n, Map.insert name n fields )
207+ Map.Map Word8 (Map.Map Name.Name Int)
210+lookupEnum :: Word8 -> Name.Name -> Enums -> (Int, Enums)
211+lookupEnum word var allEnums =
214+ Map.findWithDefault Map.empty word allEnums
216+ case Map.lookup var enums of
221+ let n = Map.size enums in
222+ ( n, Map.insert word (Map.insert var n enums) allEnums )
@@ -64,16 +227,16 @@ parser importDict =
66type VarTable =
229type VarTable =
67− Map.Map Name.Name Opt.KChunk
230+ Map.Map Name.Name Chunk
70−data State =
233+type State =
71− State VarTable (Set.Set Opt.Global)
234+ (VarTable, Set.Set Dep)
74processImports :: ImportDict -> [Src.Import] -> State
237processImports :: ImportDict -> [Src.Import] -> State
75processImports importDict imports =
238processImports importDict imports =
76− List.foldl' (addImport importDict) (State Map.empty Set.empty) imports
239+ List.foldl' (addImport importDict) (Map.empty, Set.empty) imports
79addImport :: ImportDict -> State -> Src.Import -> State
242addImport :: ImportDict -> State -> Src.Import -> State
@@ -81,7 +244,7 @@ addImport importDict state (Src.Import (A.At _ home) maybeAlias exposing) =
81 if Name.isKernel home then
244 if Name.isKernel home then
82 case maybeAlias of
245 case maybeAlias of
83 Just _ ->
246 Just _ ->
84− error ("Cannot use aliases on kernel import of: " ++ Name.toString home)
247+ error ("Cannot use aliases on kernel import of: " ++ Name.toChars home)
86 Nothing ->
249 Nothing ->
87 addKernelImport (Name.getKernel home) exposing state
250 addKernelImport (Name.getKernel home) exposing state
@@ -96,29 +259,29 @@ addImport importDict state (Src.Import (A.At _ home) maybeAlias exposing) =
96 addNormalImport canonicalHome prefix exposing state
259 addNormalImport canonicalHome prefix exposing state
99− error ("Cannot find kernel import of: " ++ Name.toString home)
262+ error ("Cannot find kernel import of: " ++ Name.toChars home)
102-- INVARIANT: the `home` is the * in `Elm.Kernel.*`
265-- INVARIANT: the `home` is the * in `Elm.Kernel.*`
104addKernelImport :: Name.Name -> Src.Exposing -> State -> State
267addKernelImport :: Name.Name -> Src.Exposing -> State -> State
105−addKernelImport home exposing (State vtable deps) =
268+addKernelImport home exposing (vtable, deps) =
107 addVar table name =
270 addVar table name =
108− Map.insert (Name.sepBy 0x5F {- _ -} home name) (Opt.JsVar home name) table
271+ Map.insert (Name.sepBy 0x5F {-_-} home name) (JsVar home name) table
111− (List.foldl' addVar vtable (toNames exposing))
273+ ( List.foldl' addVar vtable (toNames exposing)
112− (Set.insert (Opt.kernel home) deps)
274+ , Set.insert (KernelDep home) deps
115addNormalImport :: ModuleName.Canonical -> Name.Name -> Src.Exposing -> State -> State
278addNormalImport :: ModuleName.Canonical -> Name.Name -> Src.Exposing -> State -> State
116addNormalImport home prefix exposing state =
279addNormalImport home prefix exposing state =
118− addVar (State vtable deps) name =
281+ addVar (vtable, deps) name =
120− (Map.insert (Name.sepBy 0x5F {- _ -} prefix name) (Opt.ElmVar home name) vtable)
282+ ( Map.insert (Name.sepBy 0x5F {-_-} prefix name) (ElmVar home name) vtable
121− (Set.insert (Opt.Global home name) deps)
283+ , Set.insert (ElmDep home name) deps
123 List.foldl' addVar state (toNames exposing)
286 List.foldl' addVar state (toNames exposing)
@@ -131,10 +294,11 @@ toPrefix home maybeAlias =
132 Nothing ->
295 Nothing ->
133 if Name.hasDot home then
296 if Name.hasDot home then
134− error ("kernel imports with dots need an alias: " ++ show (Name.toString home))
297+ error ("kernel imports with dots need an alias: " ++ show (Name.toChars home))
138toNames :: Src.Exposing -> [Name.Name]
302toNames :: Src.Exposing -> [Name.Name]
139toNames exposing =
303toNames exposing =
140 case exposing of
304 case exposing of
@@ -162,87 +326,35 @@ toName (A.At _ exposed) =
165−-- PARSE CHUNKS
329+-- BINARY
168−chompChunks :: VarTable -> Enums -> Fields -> [Opt.KChunk] -> Parser [Opt.KChunk]
169−chompChunks vtable enums fields chunks =
170− do (javascript, maybeTag) <- K.chunk
173− return (Opt.JS javascript : chunks)
178− chompChunks vtable enums fields $
179− Opt.Prod : Opt.JS javascript : chunks
182− chompChunks vtable enums fields $
183− Opt.Debug : Opt.JS javascript : chunks
186− case Map.lookup var vtable of
188− error ("Bad kernel symbol: " ++ Name.toString var)
191− chompChunks vtable enums fields $
192− chunk : Opt.JS javascript : chunks
195− let (enum, newEnums) = lookupEnum n var enums in
196− chompChunks vtable newEnums fields $
197− Opt.JsEnum enum : Opt.JS javascript : chunks
200− chompChunks vtable enums fields $
201− Opt.ElmField name : Opt.JS javascript : chunks
204− let (field, newFields) = lookupField name fields in
205− chompChunks vtable enums newFields $
206− Opt.JsField field : Opt.JS javascript : chunks
214− Map.Map Name.Name Int
217−lookupField :: Name.Name -> Fields -> (Int, Fields)
218−lookupField name fields =
219− case Map.lookup name fields of
224− let n = Map.size fields in
225− ( n, Map.insert name n fields )
232−type Enums =
332+instance Binary Chunk where
233− Map.Map Word8 (Map.Map Name.Name Int)
333+ put chunk =
335+ JS a -> putWord8 0 >> put (coerce a :: Utf8.String)
336+ ElmVar a b -> putWord8 1 >> put a >> put b
337+ JsVar a b -> putWord8 2 >> put a >> put b
338+ ElmField a -> putWord8 3 >> put a
339+ JsField a -> putWord8 4 >> put a
340+ JsEnum a -> putWord8 5 >> put a
348+ 1 -> liftM2 ElmVar get get
349+ 2 -> liftM2 JsVar get get
350+ 3 -> liftM ElmField get
351+ 4 -> liftM JsField get
352+ 5 -> liftM JsEnum get
355+ _ -> error "problem deserializing Elm.Kernel.Chunk"
236−lookupEnum :: Word8 -> Name.Name -> Enums -> (Int, Enums)
237−lookupEnum word var allEnums =
240− Map.findWithDefault Map.empty word allEnums
242− case Map.lookup var enums of
246− Nothing ->
358+toJS :: Utf8.String -> Chunk
247− let n = Map.size enums in
359+toJS str =
248− ( n, Map.insert word (Map.insert var n enums) allEnums )
360+ JS (coerce str)