File: reader_macros.hy

package info (click to toggle)
hy 1.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,632 kB
  • sloc: python: 7,299; makefile: 38; sh: 27
file content (151 lines) | stat: -rw-r--r-- 5,454 bytes parent folder | download | duplicates (2)
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
(import
  itertools
  sys
  types
  contextlib [contextmanager]
  hy.errors [HyMacroExpansionError]

  pytest)

(defn [contextmanager] temp-module [module-name]
  (let [module (types.ModuleType module-name)
        old-module (sys.modules.get module-name)]
    (setv (get sys.modules module-name) module)
    (try
      (yield module)
      (finally
        (if old-module
            (setv (get sys.modules module-name) module)
            (sys.modules.pop module-name))))))


(defn eval-isolated [tree [module None]]
  (if module
      (hy.eval tree :locals {} :module module)
      (with [module (temp-module "<test>")]
        (hy.eval tree :locals {} :module module))))

(defn eval-module [s]
  (eval-isolated (hy.read-many s)))

(defn test-reader-macros []
  (assert (= (eval-module #[[(defreader foo '1) #foo]]) 1))
  (assert (in "foo"
    (eval-module #[[(defreader foo '1) _hy_reader_macros]])))
  (assert (= (eval-module #[[(defreader ^foo '1) #^foo]]) 1))

  (assert (not-in "rm___x"
    (eval-module
      #[[(defreader rm---x '1)
         _hy_reader_macros]])))

  ;; Assert reader macros operating exclusively at read time
  (with [module (temp-module "<test>")]
    (setv it (hy.read-many #[reader[
                              (defreader lower
                                (hy.models.String
                                  (.lower (&reader.parse-one-form))))
                              #lower "HeLLO, WoRLd!"
                           ]reader]))
    (eval-isolated (next it) module)
    (assert (= (next it) '"hello, world!"))))

(defn test-bad-reader-macro-name []
  (with [(pytest.raises HyMacroExpansionError)]
    (eval-module "(defreader :a-key '1)"))
  (with [(pytest.raises hy.PrematureEndOfInput)]
      (eval-module "# _ 3")))

(defn test-get-macro []
  (assert (eval-module #[[
    (defreader rm1
      11)
    (defreader rm☘
      22)
    (and
      (is (get-macro :reader rm1) (get _hy_reader_macros "rm1"))
      (is (get-macro :reader rm☘) (get _hy_reader_macros "rm☘")))]])))

(defn test-docstring []
  (assert (=
    (eval-module #[[
      (defreader foo
        "docstring of foo"
        15)
      #(#foo (. (get-macro :reader foo) __doc__))]])
    #(15 "docstring of foo"))))

(defn test-require-readers []
  (with [module (temp-module "<test>")]
    (setv it (hy.read-many #[[(require tests.resources.tlib :readers [upper!])
                             #upper! hello]]))
    (eval-isolated (next it) module)
    (assert (= (next it) 'HELLO)))

  ;; test require :readers & :macros is order independent
  (for [s ["[qplah] :readers [upper!]"
           ":readers [upper!] [qplah]"
           ":macros [qplah] :readers [upper!]"
           ":readers [upper!] :macros [qplah]"]]
    (assert (=
      (eval-module #[f[
        (require tests.resources.tlib {s})
        [(qplah 1) #upper! "hello"]]f])
      [[8 1] "HELLO"])))

  ;; test require :readers *
  (assert (=
      (eval-module #[=[
        (require tests.resources.tlib :readers *)
        [#upper! "eVeRy" #lower "ReAdEr"]]=])
      ["EVERY" "reader"]))

  ;; test can't redefine :macros or :readers assignment brackets
  (with [(pytest.raises hy.errors.HySyntaxError)]
    (eval-module #[[(require tests.resources.tlib [taggart] [upper!])]]))
  (with [(pytest.raises hy.errors.HySyntaxError)]
    (eval-module #[[(require tests.resources.tlib :readers [taggart] :readers [upper!])]]))
  (with [(pytest.raises hy.errors.HyRequireError)]
    (eval-module #[[(require tests.resources.tlib :readers [not-a-real-reader])]])))

(defn test-eval-read []
  ;; https://github.com/hylang/hy/issues/2291
  ;; hy.eval should not raise an exception when
  ;; defining readers using hy.read or with quoted forms
  (with [module (temp-module "<test>")]
    (hy.eval (hy.read "(defreader r 5)") :module module)
    (hy.eval '(defreader test-read 4) :module module)
    (hy.eval '(require tests.resources.tlib :readers [upper!]) :module module)
    ;; these reader macros should not exist in any current reader
    (for [tag #("#r" "#test-read" "#upper!")]
      (with [(pytest.raises hy.errors.HySyntaxError)]
        (hy.read tag)))
    ;; but they should be installed in the module
    (hy.eval '(setv reader (hy.HyReader :use-current-readers True)) :module module)
    (setv reader module.reader)
    (for [[s val] [["#r" 5]
                   ["#test-read" 4]
                   ["#upper! \"hi there\"" "HI THERE"]]]
      (assert (= (hy.eval (hy.read s :reader reader) :module module) val))))

  ;; passing a reader explicitly should work as expected
  (with [module (temp-module "<test>")]
    (setv reader (hy.HyReader))
    (defn eval1 [s]
      (hy.eval (hy.read s :reader reader) :module module))
    (eval1 "(defreader fbaz 32)")
    (eval1 "(require tests.resources.tlib :readers [upper!])")
    (assert (= (eval1 "#fbaz") 32))
    (assert (= (eval1 "#upper! \"hello\"") "HELLO"))))


(defn test-interleaving-readers []
  (with [module1 (temp-module "<one>")
         module2 (temp-module "<two>")]
    (setv stream1 (hy.read-many #[[(do (defreader foo "foo1") (defreader bar "bar1")) #foo #bar]])
          stream2 (hy.read-many #[[(do (defreader foo "foo2") (defreader bar "bar2")) #foo #bar]])
          valss [[None None] ["foo1" "foo2"] ["bar1" "bar2"]])
    (for [[form1 form2 vals] (zip stream1 stream2 valss)]
      (assert (= vals
                 [(hy.eval form1 :module module1)
                  (hy.eval form2 :module module2)])))))