aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/lux/macro/poly/text-encoder.lux
blob: c2ab30d7fd4fdde3aec27bf1220b66be3769a916 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
##  Copyright (c) Eduardo Julian. All rights reserved.
##  This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
##  If a copy of the MPL was not distributed with this file,
##  You can obtain one at http://mozilla.org/MPL/2.0/.

(;module:
  lux
  (lux (control monad
                [codec])
       (data [text]
             text/format
             (struct [list "List/" Monad<List>]
                     [dict #+ Dict])
             [number]
             [product]
             [bool]
             [char]
             [maybe]
             [ident "Ident/" Codec<Text,Ident>]
             error)
       [compiler #+ Monad<Lux> with-gensyms]
       (macro [ast]
              [syntax #+ syntax: Syntax]
              (syntax [common])
              [poly #+ poly:])
       [type]
       ))

## [Derivers]
(poly: #export (|Codec@Text//encode| env :x:)
  (let [->Codec//encode (: (-> AST AST)
                           (lambda [.type.] (` (-> (~ .type.) Text))))]
    (let% [<basic> (do-template [<type> <matcher> <encoder>]
                     [(do @
                        [_ (<matcher> :x:)]
                        (wrap (` (: (~ (->Codec//encode (` <type>)))
                                    (~' <encoder>)))))]

                     [Unit poly;unit (lambda [_0] "[]")]
                     [Bool poly;bool (:: bool;Codec<Text,Bool> encode)]
                     [Nat  poly;nat  (:: number;Codec<Text,Nat> encode)]
                     [Int  poly;int  (:: number;Codec<Text,Int> encode)]
                     [Frac poly;frac (:: number;Codec<Text,Frac> encode)]
                     [Real poly;real (:: number;Codec<Text,Real> encode)]
                     [Char poly;char (:: char;Codec<Text,Char> encode)]
                     [Text poly;text (:: text;Codec<Text,Text> encode)])]
      ($_ compiler;either
          ## Primitives
          <basic>
          ## Variants
          (with-gensyms [g!type-fun g!case g!input]
            (do @
              [[g!vars cases] (poly;variant :x:)
               #let [new-env (poly;extend-env g!type-fun g!vars env)]
               pattern-matching (mapM @
                                      (lambda [[name :case:]]
                                        (do @
                                          [encoder (|Codec@Text//encode| new-env :case:)]
                                          (wrap (list (` ((~ (ast;tag name)) (~ g!case)))
                                                      (` (format "(#"
                                                                 (~ (ast;text (Ident/encode name)))
                                                                 " "
                                                                 ((~ encoder) (~ g!case))
                                                                 ")"))))))
                                      cases)]
              (wrap (` (: (~ (poly;gen-type ->Codec//encode g!type-fun g!vars :x:))
                          (lambda [(~@ g!vars)]
                            (lambda [(~ g!input)]
                              (case (~ g!input)
                                (~@ (List/join pattern-matching)))))
                          )))))
          ## Records
          (with-gensyms [g!type-fun g!case g!input]
            (do @
              [[g!vars slots] (poly;record :x:)
               #let [new-env (poly;extend-env g!type-fun g!vars env)]
               synthesis (mapM @
                               (lambda [[name :slot:]]
                                 (do @
                                   [encoder (|Codec@Text//encode| new-env :slot:)]
                                   (wrap (` (format "#"
                                                    (~ (ast;text (Ident/encode name)))
                                                    " "
                                                    ((~ encoder) (get@ (~ (ast;tag name)) (~ g!input))))))))
                               slots)]
              (wrap (` (: (~ (poly;gen-type ->Codec//encode g!type-fun g!vars :x:))
                          (lambda [(~@ g!vars)]
                            (lambda [(~ g!input)]
                              (format "{" (~@ (list;interpose (' " ") synthesis)) "}")))
                          )))))
          ## Tuples
          (with-gensyms [g!type-fun g!case g!input]
            (do @
              [[g!vars members] (poly;tuple :x:)
               #let [new-env (poly;extend-env g!type-fun g!vars env)]
               parts (mapM @
                           (lambda [:member:]
                             (do @
                               [g!member (compiler;gensym "g!member")
                                encoder (|Codec@Text//encode| new-env :member:)]
                               (wrap [g!member encoder])))
                           members)
               #let [analysis (` [(~@ (List/map product;left parts))])
                     synthesis (List/map (lambda [[g!member g!encoder]]
                                           (` ((~ g!encoder) (~ g!member))))
                                         parts)]]
              (wrap (` (: (~ (poly;gen-type ->Codec//encode g!type-fun g!vars :x:))
                          (lambda [(~@ g!vars)]
                            (lambda [(~ g!input)]
                              (case (~ g!input)
                                (~ analysis)
                                (format "[" (~@ (list;interpose (' " ") synthesis)) "]"))))
                          )))
              ))
          ## Type applications
          (do @
            [[:func: :args:] (poly;apply :x:)
             .func. (|Codec@Text//encode| env :func:)
             .args. (mapM @ (|Codec@Text//encode| env) :args:)]
            (wrap (` (: (~ (->Codec//encode (type;to-ast :x:)))
                        ((~ .func.) (~@ .args.))))))
          ## Bound type-variables
          (poly;bound env :x:)
          ## Failure...
          (compiler;fail (format "Can't create Text encoder for: " (%type :x:)))
          ))))