@@ -6,7 +6,7 @@ import Control.Arrow (first)
66import Control.Monad (liftM,(<=<),join,ap)
77import Data.Char (isAlpha,isDigit)
88import Data.List (intercalate,sortBy,inits,foldl')
9−import Data.Map (toList)
9+import qualified Data.Map as Map
1010import Data.Either (partitionEithers)
1111import qualified Text.Pandoc as Pan
1212
@@ -175,54 +175,75 @@ toJS' (C txt span expr) =
175175 Case e cases -> caseToJS span e cases
176176 _ -> toJS expr
177177
178+recordToJS e loop cmds =
179+ do e' <- toJS' e
180+ return $ jsFunc "" (concat [ assign "r" "{_:[true]}"
181+ , assign "e" e'
182+ , "\nfor(var i in e){", loop, "}"
183+ , cmds
184+ , ret "r" ]) ++ "()"
185+
186+remove x = concat [ "\n if (i!='", x, "') { r[i]=e[i]; }"
187+ , "\n else if (e[i].length>1) { r[i]=e[i].slice(1); }" ]
188+addField (x,e) = ((++add) . assign "v") `liftM` toJS' e
189+ where add = concat [ "\nif (r.hasOwnProperty('", x, "')) {"
190+ , "\n r.", x, " = r.", x, ".slice(0);"
191+ , "\n r.", x, ".unshift(v);"
192+ , "\n} else { r.", x, " = [v]; }" ]
193+setField (x,e) = do
194+ set <- globalAssign ("r." ++ x ++ "[0]") `liftM` toJS' e
195+ return (globalAssign ("r." ++ x) ("r." ++ x ++ ".slice(0)") ++ set)
196+access x e = jsFunc "r" (ret body) ++ parens e
197+ where body = "r.hasOwnProperty('_') ? r." ++ x ++ "[0] : r." ++ x
198+makeRecord kvs = do
199+ kvs' <- (Map.toList . foldl' combine Map.empty) `liftM` mapM prep kvs
200+ let fs = map (\(k,vs) -> k ++ " : " ++ jsList vs) kvs' ++ ["_ : [true]"]
201+ return $ "\n " ++ braces (intercalate ",\n " fs)
202+ where combine r (k,v) = Map.insertWith (++) k v r
203+ prep (k, as, e@(C t s _)) =
204+ do v <- toJS' (foldr (\x e -> C t s $ Lambda x e) e as)
205+ return (k,[v])
206+
207+
178208instance ToJS Expr where
179− toJS expr =
180− case expr of
181− IntNum n -> return $ show n
182− FloatNum n -> return $ show n
183− Var x -> return $ x
184− Chr c -> return $ quoted [c]
185− Str s -> return $ "Value.str" ++ parens (quoted s)
186− Boolean b -> return $ if b then "true" else "false"
187− Range lo hi -> jsRange `liftM` toJS' lo `ap` toJS' hi
188− Access e lbl -> (\s -> s ++ "." ++ lbl) `liftM` toJS' e
189−
190− Modify e lbl v -> do e' <- toJS' e
191− v' <- toJS' v
192− let body = concat [ assign "r" e'
193− , globalAssign ("r." ++ lbl) v'
194− , ret "r" ]
195− return $ jsFunc "" body ++ "()"
196−
197− Record fs -> (braces . intercalate ",\n") `liftM` mapM toField fs
198− where toField (f, as, ce@(C t s e)) =
199− do v <- toJS' (foldr (\x e -> C t s $ Lambda x e) ce as)
200− return (f ++ " : " ++ v)
201−
202− Binop op e1 e2 -> binop op `liftM` toJS' e1 `ap` toJS' e2
203−
204− If eb et ef ->
205− parens `liftM` (iff `liftM` toJS' eb `ap` toJS' et `ap` toJS' ef)
206−
207− Lambda v e -> liftM (jsFunc v . ret) (toJS' e)
208−
209− App (C _ _ (Var "toText")) (C _ _ (Str s)) ->
210− return $ "toText" ++ parens (quoted s)
211−
212− App (C _ _ (Var "link")) (C _ _ (Str s)) ->
213− return $ "link(" ++ quoted s ++ ")"
214−
215− App (C _ _ (Var "plainText")) (C _ _ (Str s)) ->
216− return $ "plainText(" ++ quoted s ++ ")"
217−
218− App e1 e2 -> (++) `liftM` (toJS' e1) `ap` (parens `liftM` toJS' e2)
219− Let defs e -> jsLet defs e
220− Data name es -> (\ss -> jsList $ quoted name : ss) `liftM` mapM toJS' es
221−
222− Markdown doc -> return $ "text('" ++ pad ++ md ++ pad ++ "')"
223− where pad = "<div style=\"height:0;width:0;\"> </div>"
224− md = formatMarkdown $
225− Pan.writeHtmlString Pan.defaultWriterOptions doc
209+ toJS expr =
210+ case expr of
211+ IntNum n -> return $ show n
212+ FloatNum n -> return $ show n
213+ Var x -> return $ x
214+ Chr c -> return $ quoted [c]
215+ Str s -> return $ "Value.str" ++ parens (quoted s)
216+ Boolean b -> return $ if b then "true" else "false"
217+ Range lo hi -> jsRange `liftM` toJS' lo `ap` toJS' hi
218+ Access e x -> access x `liftM` toJS' e
219+ Remove e x -> recordToJS e (remove x) ""
220+ Insert e x v -> recordToJS e "r[i]=e[i];" =<< addField (x,v)
221+ Modify e fs -> recordToJS e "r[i]=e[i];" . concat =<< mapM setField fs
222+ Record fs -> makeRecord fs
223+ Binop op e1 e2 -> binop op `liftM` toJS' e1 `ap` toJS' e2
224+
225+ If eb et ef ->
226+ parens `liftM` (iff `liftM` toJS' eb `ap` toJS' et `ap` toJS' ef)
227+
228+ Lambda v e -> liftM (jsFunc v . ret) (toJS' e)
229+
230+ App (C _ _ (Var "toText")) (C _ _ (Str s)) ->
231+ return $ "toText" ++ parens (quoted s)
232+
233+ App (C _ _ (Var "link")) (C _ _ (Str s)) ->
234+ return $ "link(" ++ quoted s ++ ")"
235+
236+ App (C _ _ (Var "plainText")) (C _ _ (Str s)) ->
237+ return $ "plainText(" ++ quoted s ++ ")"
238+
239+ App e1 e2 -> (++) `liftM` (toJS' e1) `ap` (parens `liftM` toJS' e2)
240+ Let defs e -> jsLet defs e
241+ Data name es -> (\ss -> jsList $ quoted name : ss) `liftM` mapM toJS' es
242+
243+ Markdown doc -> return $ "text('" ++ pad ++ md ++ pad ++ "')"
244+ where pad = "<div style=\"height:0;width:0;\"> </div>"
245+ md = formatMarkdown $
246+ Pan.writeHtmlString Pan.defaultWriterOptions doc
226247
227248formatMarkdown = concatMap f
228249 where f '\'' = "\\'"