0 | module Text.WebIDL.Codegen.Util
  1 |
  2 | import Data.List
  3 | import Data.Nat
  4 | import Data.Stream
  5 | import public Data.String
  6 | import public Data.Vect
  7 | import public Text.PrettyPrint.Bernardy
  8 | import public Text.WebIDL.Codegen.Types
  9 | import public Text.WebIDL.Types
 10 |
 11 | %default total
 12 |
 13 | export
 14 | mapFirstChar : (Char -> Char) -> String -> String
 15 | mapFirstChar f x = case unpack x of
 16 |   []     => ""
 17 |   h :: t => pack (f h :: t)
 18 |
 19 | --------------------------------------------------------------------------------
 20 | --          Sorted Lists
 21 | --------------------------------------------------------------------------------
 22 |
 23 | export
 24 | sortedNubOn : Ord b => (a -> b) -> List a -> List a
 25 | sortedNubOn f = nub . sortBy (comparing f)
 26 |
 27 |   where
 28 |     nub : List a -> List a
 29 |     nub (x :: t@(y :: ys)) = if f x == f y then nub t else x :: nub t
 30 |     nub xs                 = xs
 31 |
 32 | --------------------------------------------------------------------------------
 33 | --          Modules
 34 | --------------------------------------------------------------------------------
 35 |
 36 | export
 37 | moduleName : String -> String
 38 | moduleName "uievents" = "UIEvents"
 39 | moduleName s          = mapFirstChar toUpper s
 40 |
 41 | --------------------------------------------------------------------------------
 42 | --          String Literals
 43 | --------------------------------------------------------------------------------
 44 |
 45 | export
 46 | unquote : String -> List Char
 47 | unquote = run . unpack
 48 |
 49 |   where
 50 |     run : List Char -> List Char
 51 |     run []                   = []
 52 |     run ('\\' :: '"' :: cs)  = '"' :: run cs
 53 |     run ('"' :: cs)          = run cs
 54 |     run (c   :: cs)          = c :: run cs
 55 |
 56 | ||| Generates a data constructor from a string literal.
 57 | ||| This is used for enums, where some values are not
 58 | ||| valid idris identifiers. Some necessary adjustments
 59 | ||| are hardcoded here.
 60 | export
 61 | toDataConstructor : String -> String
 62 | toDataConstructor s = case unquote s of
 63 |   []        => "Empty"
 64 |   ['2','d'] => "TwoD"
 65 |   c :: cs   => pack (toUpper c :: run cs)
 66 |
 67 |   where
 68 |     run : List Char -> List Char
 69 |     run []             = []
 70 |     run (x :: xs@(c :: cs)) =
 71 |       if isAlphaNum x then x :: run xs else toUpper c :: run cs
 72 |     run (c :: cs)      = c :: run cs
 73 |
 74 | --------------------------------------------------------------------------------
 75 | --          Comments
 76 | --------------------------------------------------------------------------------
 77 |
 78 | export
 79 | title : String -> String
 80 | title n = """
 81 |   --------------------------------------------------------------------------------
 82 |   --          \{n}
 83 |   --------------------------------------------------------------------------------
 84 |   """
 85 |
 86 | export
 87 | section : String -> List String -> String
 88 | section _ Nil = ""
 89 | section t ds  = unlines ("" :: title t :: ds)
 90 |
 91 | --------------------------------------------------------------------------------
 92 | --          Namespaces Implementations
 93 | --------------------------------------------------------------------------------
 94 |
 95 | export
 96 | namespaced : Identifier -> List String -> String
 97 | namespaced _ [] = ""
 98 | namespaced n ds = unlines $ "" :: "namespace \{n.value}" :: ds
 99 |
100 | --------------------------------------------------------------------------------
101 | --          Generating Functions
102 | --------------------------------------------------------------------------------
103 |
104 | parameters {opts : LayoutOpts}
105 |
106 |   export
107 |   functionTypeOnly : (res : Doc opts) -> (args : List $ Doc opts) -> Doc opts
108 |   functionTypeOnly res []     = parens $ "() ->" <++> res
109 |   functionTypeOnly res (h::t) =
110 |     let withArrows := map (line "->" <++>) (t ++ [res])
111 |         sl         := parens (hsep $ h :: withArrows)
112 |         ml         := vsep $ (line "(  " <+> h) :: withArrows ++ [line ")"]
113 |      in ifMultiline sl ml
114 |
115 |   export
116 |   functionType :
117 |        (name : IdrisIdent)
118 |     -> (sep  : String)
119 |     -> (res  : Doc opts)
120 |     -> (args : List $ Doc opts)
121 |     -> Doc opts
122 |   functionType nm sep res [] =
123 |     let head := line "\{nm} \{sep}"
124 |      in ifMultiline (head <++> res) (vappend head $ indent 2 res)
125 |   functionType nm sep res (h::t) =
126 |     let head       := line "\{nm} \{sep}"
127 |         withArrows := map (line "->" <++>) (t ++ [res])
128 |         sl         := hsep $ head :: h :: withArrows
129 |         ml         := vsep $ indent 3 h :: withArrows
130 |      in ifMultiline sl (vappend head $ indent 2 ml)
131 |
132 |   export
133 |   typeDecl :
134 |        (name : IdrisIdent)
135 |     -> (res : Doc opts)
136 |     -> (args : List $ Doc opts)
137 |     -> Doc opts
138 |   typeDecl n = functionType n ":"
139 | --
140 | --------------------------------------------------------------------------------
141 | --          Function Names
142 | --------------------------------------------------------------------------------
143 |
144 | ix : Nat -> String
145 | ix Z = ""
146 | ix k = show k
147 |
148 | export
149 | primSetter : Nat -> IdrisIdent
150 | primSetter k = Prim . fromString $ "set" ++ ix k
151 |
152 | export
153 | setter : Nat -> IdrisIdent
154 | setter k = fromString $ "set" ++ ix k
155 |
156 | export
157 | primGetter : Nat -> IdrisIdent
158 | primGetter k = Prim . fromString $ "get" ++ ix k
159 |
160 | export
161 | getter : Nat -> IdrisIdent
162 | getter k = fromString $ "get" ++ ix k
163 |
164 | export
165 | primAttrSetter : Nat -> AttributeName -> IdrisIdent
166 | primAttrSetter k n =
167 |   Prim $ fromString ("set" ++ mapFirstChar toUpper n.value ++ ix k)
168 |
169 | export
170 | attrSetter : Nat -> AttributeName -> IdrisIdent
171 | attrSetter k n = fromString $ "set" ++ mapFirstChar toUpper n.value ++ ix k
172 |
173 | export
174 | primAttrGetter : Nat -> AttributeName -> IdrisIdent
175 | primAttrGetter k n = Prim $ fromString (n.value ++ ix k)
176 |
177 | export
178 | attrGetter : Nat -> AttributeName -> IdrisIdent
179 | attrGetter k n = fromString $ n.value ++ ix k
180 |
181 | export
182 | primOp : Nat -> OperationName -> IdrisIdent
183 | primOp k n = Prim $ fromString (n.value ++ ix k)
184 |
185 | export
186 | op : Nat -> OperationName -> IdrisIdent
187 | op k n = fromString (n.value ++ ix k)
188 |
189 | export
190 | primConstr : Nat -> IdrisIdent
191 | primConstr k = Prim $ fromString ("new" ++ ix k)
192 |
193 | export
194 | constr : Nat -> IdrisIdent
195 | constr k = fromString ("new" ++ ix k)
196 |
197 | export
198 | marshallCallback : Identifier -> IdrisIdent
199 | marshallCallback i = fromString $ "to" ++ i.value
200 |
201 | export
202 | primMarshallCallback : Identifier -> IdrisIdent
203 | primMarshallCallback i = Prim . fromString $ "to" ++ i.value
204 |
205 | --------------------------------------------------------------------------------
206 | --          Foreign Function Implementations
207 | --------------------------------------------------------------------------------
208 |
209 | export
210 | argNames : Stream String
211 | argNames =
212 |   "a" :: "b" :: "c" :: "d" :: "e" :: "f" :: "g" ::
213 |   "h" :: "i" :: "j" :: "k" :: "l" :: "m" :: "n" ::
214 |   "o" :: "p" :: "q" :: "r" :: "s" :: "t" :: "u" ::
215 |   "v" :: "w" :: "y" :: "z" ::
216 |   map (\v => "x" ++ show {ty = Integer} v) [1 ..]
217 |
218 | export
219 | unShadowingArgNames : IdrisIdent -> Stream String
220 | unShadowingArgNames i = go "\{i}" argNames
221 |   where
222 |     go : String -> Stream String -> Stream String
223 |     go s (h :: t) = if s == h then t else h :: go s t
224 |
225 | foreignBrowser : String -> String
226 | foreignBrowser s = "%foreign \"browser:lambda:\{s}\""
227 |
228 | export
229 | attrGetFFI : AttributeName -> String
230 | attrGetFFI n = foreignBrowser "x=>x.\{n.value}"
231 |
232 | export
233 | staticAttrGetFFI : Kind -> AttributeName -> String
234 | staticAttrGetFFI o n =
235 |   foreignBrowser "()=>\{kindToString o}.\{n.value}"
236 |
237 | export
238 | attrSetFFI : AttributeName -> String
239 | attrSetFFI n = foreignBrowser "(x,v)=>{x.\{n.value} = v}"
240 |
241 | export
242 | staticAttrSetFFI : Kind -> AttributeName -> String
243 | staticAttrSetFFI o n =
244 |   foreignBrowser "v=>{\{kindToString o}.\{n.value} = v}"
245 |
246 | export
247 | funFFI : OperationName -> Nat -> String
248 | funFFI n Z = foreignBrowser "x=>x.\{n.value}()"
249 | funFFI n k =
250 |   let vs   := take k argNames
251 |       vals := fastConcat $ intersperse "," vs
252 |    in foreignBrowser "(x,\{vals})=>x.\{n.value}(\{vals})"
253 |
254 | export
255 | funFFIVarArg : OperationName -> Nat -> String
256 | funFFIVarArg n k =
257 |   let vs   := take (pred k) argNames
258 |       vals := fastConcat $ intersperse "," (vs ++ ["va"])
259 |       args := fastConcat $ intersperse "," (vs ++ ["...va()"])
260 |    in foreignBrowser "(x,\{vals})=>x.\{n.value}(\{args})"
261 |
262 | export
263 | staticFunFFI : Kind -> OperationName -> Nat -> String
264 | staticFunFFI o n Z = foreignBrowser "x=>x.\{n.value}()"
265 | staticFunFFI o n k =
266 |   let vs   := take k argNames
267 |       vals := fastConcat $ intersperse "," vs
268 |    in foreignBrowser "(\{vals})=>\{kindToString o}.\{n.value}(\{vals})"
269 |
270 | export
271 | staticFunFFIVarArg : Kind -> OperationName -> Nat -> String
272 | staticFunFFIVarArg o n k =
273 |   let vs   := take (pred k) argNames
274 |       vals := fastConcat $ intersperse "," (vs ++ ["va"])
275 |       args := fastConcat $ intersperse "," (vs ++ ["...va()"])
276 |    in foreignBrowser "(\{vals})=>\{kindToString o}.\{n.value}(\{args})"
277 |
278 | export
279 | conFFI : Kind -> Nat -> String
280 | conFFI n k =
281 |   let vs   := take k argNames
282 |       vals := fastConcat $ intersperse "," vs
283 |    in foreignBrowser "(\{vals})=> new \{kindToString n}(\{vals})"
284 |
285 | export
286 | conFFIVarArg : Kind -> Nat -> String
287 | conFFIVarArg n k =
288 |   let vs   := take (pred k) argNames
289 |       vals := fastConcat $ intersperse "," (vs ++ ["va"])
290 |       args := fastConcat $ intersperse "," (vs ++ ["...va()"])
291 |    in foreignBrowser "(\{vals})=> new \{kindToString n}(\{args})"
292 |
293 | export
294 | dictConFFI : List ArgumentName -> String
295 | dictConFFI ns =
296 |   let vs     := take (length ns) argNames
297 |       vals   := fastConcat $ intersperse "," vs
298 |       fields := fastConcat $ intersperse "," (zipWith app vs ns)
299 |    in foreignBrowser "(\{vals})=> ({\{fields}})"
300 |
301 |   where
302 |     app : String -> ArgumentName -> String
303 |     app v a = a.value ++ ": " ++ v
304 |
305 | export
306 | getterFFI : String
307 | getterFFI = foreignBrowser "(o,x)=>o[x]"
308 |
309 | export
310 | setterFFI : String
311 | setterFFI = foreignBrowser "(o,x,v)=>o[x] = v"
312 |
313 | export
314 | callbackFFI : Nat -> String
315 | callbackFFI n =
316 |   let vs := fastConcat $ intersperse "," $ take n argNames
317 |    in foreignBrowser "x=>(\{vs})=>x(\{vs})()"
318 |
319 | --------------------------------------------------------------------------------
320 | --          Pretty Printing
321 | --------------------------------------------------------------------------------
322 |
323 | export %inline
324 | render80 : Doc (Opts 80) -> String
325 | render80 = render _
326 |