We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Attributes of literals
TL;DR: the way attributes, integers, and identifiers are parsed is a hot, hot mess.

So, right now, parsing a number or symbol looks like this:

- Lexer sees token and tags it as an identifier.
- Parser grabs token, tries to covert it to an integer, float, or fraction, and, failing that, compiles it as a symbol.

The second step means that there are a lot of corner cases that are currently quite bizarre:

```
ryan@DevPC-LX /media/ryan/stuff/hy attrerr $ ./run --spy
hy 0.11.0 using CPython(default) 2.7.6 on Linux
=> (defn f [&rest args &kwargs kw])
def f(*args, **kw):
    pass
=> 

=> 2.2  ; This is a float.
2.2
2.2
=> a.2  ; This is interpreted as getting the attribute '2' on 'a'.
a.2
Traceback (most recent call last):
  File "<input>", line 1, in <module>
NameError: name 'a' is not defined
=> 2.a  ; This is interpreted as getting the attribute 'a' on an identifier '2'.
2.a
Traceback (most recent call last):
  File "<input>", line 1, in <module>
NameError: name '2' is not defined
=> a/b  ; This is parsed as a whole identifier 'a/b'...
a/b
Traceback (most recent call last):
  File "<input>", line 1, in <module>
NameError: name 'a/b' is not defined
=> 1/2  ; ...but this is a fraction.
from hy.core.language import fraction
fraction(1L, 2L)
Fraction(1, 2)
=> (f :x? 1)  ; Here, the ? isn't converted to is_ like usual.
f(x?=1L)
=> (f :*x* 1)  ; The two stars stay uncoverted.
f(*x*=1L)
=> 
ryan@DevPC-LX /media/ryan/stuff/hy attrerr $ 
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `hy/lex/parser.py`
Content:
```
1 # Copyright 2017 the authors.
2 # This file is part of Hy, which is free software licensed under the Expat
3 # license. See the LICENSE.
4 
5 from functools import wraps
6 from ast import literal_eval
7 
8 from rply import ParserGenerator
9 
10 from hy._compat import PY3, str_type
11 from hy.models import (HyBytes, HyComplex, HyCons, HyDict, HyExpression,
12                        HyFloat, HyInteger, HyKeyword, HyList, HySet, HyString,
13                        HySymbol)
14 from .lexer import lexer
15 from .exceptions import LexException, PrematureEndOfInput
16 
17 
18 pg = ParserGenerator(
19     [rule.name for rule in lexer.rules] + ['$end'],
20     cache_id="hy_parser"
21 )
22 
23 
24 def hy_symbol_mangle(p):
25     if p.startswith("*") and p.endswith("*") and p not in ("*", "**"):
26         p = p[1:-1].upper()
27 
28     if "-" in p and p != "-":
29         p = p.replace("-", "_")
30 
31     if p.endswith("?") and p != "?":
32         p = "is_%s" % (p[:-1])
33 
34     if p.endswith("!") and p != "!":
35         p = "%s_bang" % (p[:-1])
36 
37     return p
38 
39 
40 def hy_symbol_unmangle(p):
41     # hy_symbol_mangle is one-way, so this can't be perfect.
42     # But it can be useful till we have a way to get the original
43     # symbol (https://github.com/hylang/hy/issues/360).
44     p = str_type(p)
45 
46     if p.endswith("_bang") and p != "_bang":
47         p = p[:-len("_bang")] + "!"
48 
49     if p.startswith("is_") and p != "is_":
50         p = p[len("is_"):] + "?"
51 
52     if "_" in p and p != "_":
53         p = p.replace("_", "-")
54 
55     if (all([c.isalpha() and c.isupper() or c == '_' for c in p]) and
56             any([c.isalpha() for c in p])):
57         p = '*' + p.lower() + '*'
58 
59     return p
60 
61 
62 def set_boundaries(fun):
63     @wraps(fun)
64     def wrapped(p):
65         start = p[0].source_pos
66         end = p[-1].source_pos
67         ret = fun(p)
68         ret.start_line = start.lineno
69         ret.start_column = start.colno
70         if start is not end:
71             ret.end_line = end.lineno
72             ret.end_column = end.colno
73         else:
74             ret.end_line = start.lineno
75             ret.end_column = start.colno + len(p[0].value)
76         return ret
77     return wrapped
78 
79 
80 def set_quote_boundaries(fun):
81     @wraps(fun)
82     def wrapped(p):
83         start = p[0].source_pos
84         ret = fun(p)
85         ret.start_line = start.lineno
86         ret.start_column = start.colno
87         ret.end_line = p[-1].end_line
88         ret.end_column = p[-1].end_column
89         return ret
90     return wrapped
91 
92 
93 @pg.production("main : HASHBANG real_main")
94 def main_hashbang(p):
95     return p[1]
96 
97 
98 @pg.production("main : real_main")
99 def main(p):
100     return p[0]
101 
102 
103 @pg.production("real_main : list_contents")
104 def real_main(p):
105     return p[0]
106 
107 
108 @pg.production("real_main : $end")
109 def real_main_empty(p):
110     return []
111 
112 
113 def reject_spurious_dots(*items):
114     "Reject the spurious dots from items"
115     for list in items:
116         for tok in list:
117             if tok == "." and type(tok) == HySymbol:
118                 raise LexException("Malformed dotted list",
119                                    tok.start_line, tok.start_column)
120 
121 
122 @pg.production("paren : LPAREN list_contents RPAREN")
123 @set_boundaries
124 def paren(p):
125     cont = p[1]
126 
127     # Dotted lists are expressions of the form
128     # (a b c . d)
129     # that evaluate to nested cons cells of the form
130     # (a . (b . (c . d)))
131     if len(cont) >= 3 and isinstance(cont[-2], HySymbol) and cont[-2] == ".":
132 
133         reject_spurious_dots(cont[:-2], cont[-1:])
134 
135         if len(cont) == 3:
136             # Two-item dotted list: return the cons cell directly
137             return HyCons(cont[0], cont[2])
138         else:
139             # Return a nested cons cell
140             return HyCons(cont[0], paren([p[0], cont[1:], p[2]]))
141 
142     # Warn preemptively on a malformed dotted list.
143     # Only check for dots after the first item to allow for a potential
144     # attribute accessor shorthand
145     reject_spurious_dots(cont[1:])
146 
147     return HyExpression(p[1])
148 
149 
150 @pg.production("paren : LPAREN RPAREN")
151 @set_boundaries
152 def empty_paren(p):
153     return HyExpression([])
154 
155 
156 @pg.production("list_contents : term list_contents")
157 def list_contents(p):
158     return [p[0]] + p[1]
159 
160 
161 @pg.production("list_contents : term")
162 def list_contents_single(p):
163     return [p[0]]
164 
165 
166 @pg.production("term : identifier")
167 @pg.production("term : paren")
168 @pg.production("term : dict")
169 @pg.production("term : list")
170 @pg.production("term : set")
171 @pg.production("term : string")
172 def term(p):
173     return p[0]
174 
175 
176 @pg.production("term : QUOTE term")
177 @set_quote_boundaries
178 def term_quote(p):
179     return HyExpression([HySymbol("quote"), p[1]])
180 
181 
182 @pg.production("term : QUASIQUOTE term")
183 @set_quote_boundaries
184 def term_quasiquote(p):
185     return HyExpression([HySymbol("quasiquote"), p[1]])
186 
187 
188 @pg.production("term : UNQUOTE term")
189 @set_quote_boundaries
190 def term_unquote(p):
191     return HyExpression([HySymbol("unquote"), p[1]])
192 
193 
194 @pg.production("term : UNQUOTESPLICE term")
195 @set_quote_boundaries
196 def term_unquote_splice(p):
197     return HyExpression([HySymbol("unquote_splice"), p[1]])
198 
199 
200 @pg.production("term : HASHOTHER term")
201 @set_quote_boundaries
202 def hash_other(p):
203     # p == [(Token('HASHOTHER', '#foo'), bar)]
204     st = p[0].getstr()[1:]
205     str_object = HyString(st)
206     expr = p[1]
207     return HyExpression([HySymbol("dispatch_tag_macro"), str_object, expr])
208 
209 
210 @pg.production("set : HLCURLY list_contents RCURLY")
211 @set_boundaries
212 def t_set(p):
213     return HySet(p[1])
214 
215 
216 @pg.production("set : HLCURLY RCURLY")
217 @set_boundaries
218 def empty_set(p):
219     return HySet([])
220 
221 
222 @pg.production("dict : LCURLY list_contents RCURLY")
223 @set_boundaries
224 def t_dict(p):
225     return HyDict(p[1])
226 
227 
228 @pg.production("dict : LCURLY RCURLY")
229 @set_boundaries
230 def empty_dict(p):
231     return HyDict([])
232 
233 
234 @pg.production("list : LBRACKET list_contents RBRACKET")
235 @set_boundaries
236 def t_list(p):
237     return HyList(p[1])
238 
239 
240 @pg.production("list : LBRACKET RBRACKET")
241 @set_boundaries
242 def t_empty_list(p):
243     return HyList([])
244 
245 
246 if PY3:
247     def uni_hystring(s):
248         return HyString(literal_eval(s))
249 
250     def hybytes(s):
251         return HyBytes(literal_eval('b'+s))
252 
253 else:
254     def uni_hystring(s):
255         return HyString(literal_eval('u'+s))
256 
257     def hybytes(s):
258         return HyBytes(literal_eval(s))
259 
260 
261 @pg.production("string : STRING")
262 @set_boundaries
263 def t_string(p):
264     # remove trailing quote
265     s = p[0].value[:-1]
266     # get the header
267     header, s = s.split('"', 1)
268     # remove unicode marker (this is redundant because Hy string
269     # literals already, by default, generate Unicode literals
270     # under Python 2)
271     header = header.replace("u", "")
272     # remove bytes marker, since we'll need to exclude it for Python 2
273     is_bytestring = "b" in header
274     header = header.replace("b", "")
275     # build python string
276     s = header + '"""' + s + '"""'
277     return (hybytes if is_bytestring else uni_hystring)(s)
278 
279 
280 @pg.production("string : PARTIAL_STRING")
281 def t_partial_string(p):
282     # Any unterminated string requires more input
283     raise PrematureEndOfInput("Premature end of input")
284 
285 
286 @pg.production("identifier : IDENTIFIER")
287 @set_boundaries
288 def t_identifier(p):
289     obj = p[0].value
290 
291     try:
292         return HyInteger(obj)
293     except ValueError:
294         pass
295 
296     if '/' in obj:
297         try:
298             lhs, rhs = obj.split('/')
299             return HyExpression([HySymbol('fraction'), HyInteger(lhs),
300                                  HyInteger(rhs)])
301         except ValueError:
302             pass
303 
304     try:
305         return HyFloat(obj)
306     except ValueError:
307         pass
308 
309     if obj != 'j':
310         try:
311             return HyComplex(obj)
312         except ValueError:
313             pass
314 
315     if obj.startswith(":"):
316         return HyKeyword(obj)
317 
318     obj = ".".join([hy_symbol_mangle(part) for part in obj.split(".")])
319 
320     return HySymbol(obj)
321 
322 
323 @pg.error
324 def error_handler(token):
325     tokentype = token.gettokentype()
326     if tokentype == '$end':
327         raise PrematureEndOfInput("Premature end of input")
328     else:
329         raise LexException(
330             "Ran into a %s where it wasn't expected." % tokentype,
331             token.source_pos.lineno, token.source_pos.colno)
332 
333 
334 parser = pg.build()
335
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/hy/lex/parser.py b/hy/lex/parser.py
--- a/hy/lex/parser.py
+++ b/hy/lex/parser.py
@@ -288,6 +288,24 @@
 def t_identifier(p):
     obj = p[0].value
 
+    val = symbol_like(obj)
+    if val is not None:
+        return val
+
+    if "." in obj and symbol_like(obj.split(".", 1)[0]) is not None:
+        # E.g., `5.attr` or `:foo.attr`
+        raise LexException(
+            'Cannot access attribute on anything other than a name (in '
+            'order to get attributes of expressions, use '
+            '`(. <expression> <attr>)` or `(.<attr> <expression>)`)',
+            p[0].source_pos.lineno, p[0].source_pos.colno)
+
+    return HySymbol(".".join(hy_symbol_mangle(x) for x in obj.split(".")))
+
+
+def symbol_like(obj):
+    "Try to interpret `obj` as a number or keyword."
+
     try:
         return HyInteger(obj)
     except ValueError:
@@ -312,13 +330,9 @@
         except ValueError:
             pass
 
-    if obj.startswith(":"):
+    if obj.startswith(":") and "." not in obj:
         return HyKeyword(obj)
 
-    obj = ".".join([hy_symbol_mangle(part) for part in obj.split(".")])
-
-    return HySymbol(obj)
-
 
 @pg.error
 def error_handler(token):

{"golden_diff": "diff --git a/hy/lex/parser.py b/hy/lex/parser.py\n--- a/hy/lex/parser.py\n+++ b/hy/lex/parser.py\n@@ -288,6 +288,24 @@\n def t_identifier(p):\n     obj = p[0].value\n \n+    val = symbol_like(obj)\n+    if val is not None:\n+        return val\n+\n+    if \".\" in obj and symbol_like(obj.split(\".\", 1)[0]) is not None:\n+        # E.g., `5.attr` or `:foo.attr`\n+        raise LexException(\n+            'Cannot access attribute on anything other than a name (in '\n+            'order to get attributes of expressions, use '\n+            '`(. <expression> <attr>)` or `(.<attr> <expression>)`)',\n+            p[0].source_pos.lineno, p[0].source_pos.colno)\n+\n+    return HySymbol(\".\".join(hy_symbol_mangle(x) for x in obj.split(\".\")))\n+\n+\n+def symbol_like(obj):\n+    \"Try to interpret `obj` as a number or keyword.\"\n+\n     try:\n         return HyInteger(obj)\n     except ValueError:\n@@ -312,13 +330,9 @@\n         except ValueError:\n             pass\n \n-    if obj.startswith(\":\"):\n+    if obj.startswith(\":\") and \".\" not in obj:\n         return HyKeyword(obj)\n \n-    obj = \".\".join([hy_symbol_mangle(part) for part in obj.split(\".\")])\n-\n-    return HySymbol(obj)\n-\n \n @pg.error\n def error_handler(token):\n", "issue": "Attributes of literals\nTL;DR: the way attributes, integers, and identifiers are parsed is a hot, hot mess.\r\n\r\nSo, right now, parsing a number or symbol looks like this:\r\n\r\n- Lexer sees token and tags it as an identifier.\r\n- Parser grabs token, tries to covert it to an integer, float, or fraction, and, failing that, compiles it as a symbol.\r\n\r\nThe second step means that there are a lot of corner cases that are currently quite bizarre:\r\n\r\n```\r\nryan@DevPC-LX /media/ryan/stuff/hy attrerr $ ./run --spy\r\nhy 0.11.0 using CPython(default) 2.7.6 on Linux\r\n=> (defn f [&rest args &kwargs kw])\r\ndef f(*args, **kw):\r\n    pass\r\n=> \r\n\r\n=> 2.2  ; This is a float.\r\n2.2\r\n2.2\r\n=> a.2  ; This is interpreted as getting the attribute '2' on 'a'.\r\na.2\r\nTraceback (most recent call last):\r\n  File \"<input>\", line 1, in <module>\r\nNameError: name 'a' is not defined\r\n=> 2.a  ; This is interpreted as getting the attribute 'a' on an identifier '2'.\r\n2.a\r\nTraceback (most recent call last):\r\n  File \"<input>\", line 1, in <module>\r\nNameError: name '2' is not defined\r\n=> a/b  ; This is parsed as a whole identifier 'a/b'...\r\na/b\r\nTraceback (most recent call last):\r\n  File \"<input>\", line 1, in <module>\r\nNameError: name 'a/b' is not defined\r\n=> 1/2  ; ...but this is a fraction.\r\nfrom hy.core.language import fraction\r\nfraction(1L, 2L)\r\nFraction(1, 2)\r\n=> (f :x? 1)  ; Here, the ? isn't converted to is_ like usual.\r\nf(x?=1L)\r\n=> (f :*x* 1)  ; The two stars stay uncoverted.\r\nf(*x*=1L)\r\n=> \r\nryan@DevPC-LX /media/ryan/stuff/hy attrerr $ \r\n```\n", "before_files": [{"content": "# Copyright 2017 the authors.\n# This file is part of Hy, which is free software licensed under the Expat\n# license. See the LICENSE.\n\nfrom functools import wraps\nfrom ast import literal_eval\n\nfrom rply import ParserGenerator\n\nfrom hy._compat import PY3, str_type\nfrom hy.models import (HyBytes, HyComplex, HyCons, HyDict, HyExpression,\n                       HyFloat, HyInteger, HyKeyword, HyList, HySet, HyString,\n                       HySymbol)\nfrom .lexer import lexer\nfrom .exceptions import LexException, PrematureEndOfInput\n\n\npg = ParserGenerator(\n    [rule.name for rule in lexer.rules] + ['$end'],\n    cache_id=\"hy_parser\"\n)\n\n\ndef hy_symbol_mangle(p):\n    if p.startswith(\"*\") and p.endswith(\"*\") and p not in (\"*\", \"**\"):\n        p = p[1:-1].upper()\n\n    if \"-\" in p and p != \"-\":\n        p = p.replace(\"-\", \"_\")\n\n    if p.endswith(\"?\") and p != \"?\":\n        p = \"is_%s\" % (p[:-1])\n\n    if p.endswith(\"!\") and p != \"!\":\n        p = \"%s_bang\" % (p[:-1])\n\n    return p\n\n\ndef hy_symbol_unmangle(p):\n    # hy_symbol_mangle is one-way, so this can't be perfect.\n    # But it can be useful till we have a way to get the original\n    # symbol (https://github.com/hylang/hy/issues/360).\n    p = str_type(p)\n\n    if p.endswith(\"_bang\") and p != \"_bang\":\n        p = p[:-len(\"_bang\")] + \"!\"\n\n    if p.startswith(\"is_\") and p != \"is_\":\n        p = p[len(\"is_\"):] + \"?\"\n\n    if \"_\" in p and p != \"_\":\n        p = p.replace(\"_\", \"-\")\n\n    if (all([c.isalpha() and c.isupper() or c == '_' for c in p]) and\n            any([c.isalpha() for c in p])):\n        p = '*' + p.lower() + '*'\n\n    return p\n\n\ndef set_boundaries(fun):\n    @wraps(fun)\n    def wrapped(p):\n        start = p[0].source_pos\n        end = p[-1].source_pos\n        ret = fun(p)\n        ret.start_line = start.lineno\n        ret.start_column = start.colno\n        if start is not end:\n            ret.end_line = end.lineno\n            ret.end_column = end.colno\n        else:\n            ret.end_line = start.lineno\n            ret.end_column = start.colno + len(p[0].value)\n        return ret\n    return wrapped\n\n\ndef set_quote_boundaries(fun):\n    @wraps(fun)\n    def wrapped(p):\n        start = p[0].source_pos\n        ret = fun(p)\n        ret.start_line = start.lineno\n        ret.start_column = start.colno\n        ret.end_line = p[-1].end_line\n        ret.end_column = p[-1].end_column\n        return ret\n    return wrapped\n\n\n@pg.production(\"main : HASHBANG real_main\")\ndef main_hashbang(p):\n    return p[1]\n\n\n@pg.production(\"main : real_main\")\ndef main(p):\n    return p[0]\n\n\n@pg.production(\"real_main : list_contents\")\ndef real_main(p):\n    return p[0]\n\n\n@pg.production(\"real_main : $end\")\ndef real_main_empty(p):\n    return []\n\n\ndef reject_spurious_dots(*items):\n    \"Reject the spurious dots from items\"\n    for list in items:\n        for tok in list:\n            if tok == \".\" and type(tok) == HySymbol:\n                raise LexException(\"Malformed dotted list\",\n                                   tok.start_line, tok.start_column)\n\n\n@pg.production(\"paren : LPAREN list_contents RPAREN\")\n@set_boundaries\ndef paren(p):\n    cont = p[1]\n\n    # Dotted lists are expressions of the form\n    # (a b c . d)\n    # that evaluate to nested cons cells of the form\n    # (a . (b . (c . d)))\n    if len(cont) >= 3 and isinstance(cont[-2], HySymbol) and cont[-2] == \".\":\n\n        reject_spurious_dots(cont[:-2], cont[-1:])\n\n        if len(cont) == 3:\n            # Two-item dotted list: return the cons cell directly\n            return HyCons(cont[0], cont[2])\n        else:\n            # Return a nested cons cell\n            return HyCons(cont[0], paren([p[0], cont[1:], p[2]]))\n\n    # Warn preemptively on a malformed dotted list.\n    # Only check for dots after the first item to allow for a potential\n    # attribute accessor shorthand\n    reject_spurious_dots(cont[1:])\n\n    return HyExpression(p[1])\n\n\n@pg.production(\"paren : LPAREN RPAREN\")\n@set_boundaries\ndef empty_paren(p):\n    return HyExpression([])\n\n\n@pg.production(\"list_contents : term list_contents\")\ndef list_contents(p):\n    return [p[0]] + p[1]\n\n\n@pg.production(\"list_contents : term\")\ndef list_contents_single(p):\n    return [p[0]]\n\n\n@pg.production(\"term : identifier\")\n@pg.production(\"term : paren\")\n@pg.production(\"term : dict\")\n@pg.production(\"term : list\")\n@pg.production(\"term : set\")\n@pg.production(\"term : string\")\ndef term(p):\n    return p[0]\n\n\n@pg.production(\"term : QUOTE term\")\n@set_quote_boundaries\ndef term_quote(p):\n    return HyExpression([HySymbol(\"quote\"), p[1]])\n\n\n@pg.production(\"term : QUASIQUOTE term\")\n@set_quote_boundaries\ndef term_quasiquote(p):\n    return HyExpression([HySymbol(\"quasiquote\"), p[1]])\n\n\n@pg.production(\"term : UNQUOTE term\")\n@set_quote_boundaries\ndef term_unquote(p):\n    return HyExpression([HySymbol(\"unquote\"), p[1]])\n\n\n@pg.production(\"term : UNQUOTESPLICE term\")\n@set_quote_boundaries\ndef term_unquote_splice(p):\n    return HyExpression([HySymbol(\"unquote_splice\"), p[1]])\n\n\n@pg.production(\"term : HASHOTHER term\")\n@set_quote_boundaries\ndef hash_other(p):\n    # p == [(Token('HASHOTHER', '#foo'), bar)]\n    st = p[0].getstr()[1:]\n    str_object = HyString(st)\n    expr = p[1]\n    return HyExpression([HySymbol(\"dispatch_tag_macro\"), str_object, expr])\n\n\n@pg.production(\"set : HLCURLY list_contents RCURLY\")\n@set_boundaries\ndef t_set(p):\n    return HySet(p[1])\n\n\n@pg.production(\"set : HLCURLY RCURLY\")\n@set_boundaries\ndef empty_set(p):\n    return HySet([])\n\n\n@pg.production(\"dict : LCURLY list_contents RCURLY\")\n@set_boundaries\ndef t_dict(p):\n    return HyDict(p[1])\n\n\n@pg.production(\"dict : LCURLY RCURLY\")\n@set_boundaries\ndef empty_dict(p):\n    return HyDict([])\n\n\n@pg.production(\"list : LBRACKET list_contents RBRACKET\")\n@set_boundaries\ndef t_list(p):\n    return HyList(p[1])\n\n\n@pg.production(\"list : LBRACKET RBRACKET\")\n@set_boundaries\ndef t_empty_list(p):\n    return HyList([])\n\n\nif PY3:\n    def uni_hystring(s):\n        return HyString(literal_eval(s))\n\n    def hybytes(s):\n        return HyBytes(literal_eval('b'+s))\n\nelse:\n    def uni_hystring(s):\n        return HyString(literal_eval('u'+s))\n\n    def hybytes(s):\n        return HyBytes(literal_eval(s))\n\n\n@pg.production(\"string : STRING\")\n@set_boundaries\ndef t_string(p):\n    # remove trailing quote\n    s = p[0].value[:-1]\n    # get the header\n    header, s = s.split('\"', 1)\n    # remove unicode marker (this is redundant because Hy string\n    # literals already, by default, generate Unicode literals\n    # under Python 2)\n    header = header.replace(\"u\", \"\")\n    # remove bytes marker, since we'll need to exclude it for Python 2\n    is_bytestring = \"b\" in header\n    header = header.replace(\"b\", \"\")\n    # build python string\n    s = header + '\"\"\"' + s + '\"\"\"'\n    return (hybytes if is_bytestring else uni_hystring)(s)\n\n\n@pg.production(\"string : PARTIAL_STRING\")\ndef t_partial_string(p):\n    # Any unterminated string requires more input\n    raise PrematureEndOfInput(\"Premature end of input\")\n\n\n@pg.production(\"identifier : IDENTIFIER\")\n@set_boundaries\ndef t_identifier(p):\n    obj = p[0].value\n\n    try:\n        return HyInteger(obj)\n    except ValueError:\n        pass\n\n    if '/' in obj:\n        try:\n            lhs, rhs = obj.split('/')\n            return HyExpression([HySymbol('fraction'), HyInteger(lhs),\n                                 HyInteger(rhs)])\n        except ValueError:\n            pass\n\n    try:\n        return HyFloat(obj)\n    except ValueError:\n        pass\n\n    if obj != 'j':\n        try:\n            return HyComplex(obj)\n        except ValueError:\n            pass\n\n    if obj.startswith(\":\"):\n        return HyKeyword(obj)\n\n    obj = \".\".join([hy_symbol_mangle(part) for part in obj.split(\".\")])\n\n    return HySymbol(obj)\n\n\n@pg.error\ndef error_handler(token):\n    tokentype = token.gettokentype()\n    if tokentype == '$end':\n        raise PrematureEndOfInput(\"Premature end of input\")\n    else:\n        raise LexException(\n            \"Ran into a %s where it wasn't expected.\" % tokentype,\n            token.source_pos.lineno, token.source_pos.colno)\n\n\nparser = pg.build()\n", "path": "hy/lex/parser.py"}], "after_files": [{"content": "# Copyright 2017 the authors.\n# This file is part of Hy, which is free software licensed under the Expat\n# license. See the LICENSE.\n\nfrom functools import wraps\nfrom ast import literal_eval\n\nfrom rply import ParserGenerator\n\nfrom hy._compat import PY3, str_type\nfrom hy.models import (HyBytes, HyComplex, HyCons, HyDict, HyExpression,\n                       HyFloat, HyInteger, HyKeyword, HyList, HySet, HyString,\n                       HySymbol)\nfrom .lexer import lexer\nfrom .exceptions import LexException, PrematureEndOfInput\n\n\npg = ParserGenerator(\n    [rule.name for rule in lexer.rules] + ['$end'],\n    cache_id=\"hy_parser\"\n)\n\n\ndef hy_symbol_mangle(p):\n    if p.startswith(\"*\") and p.endswith(\"*\") and p not in (\"*\", \"**\"):\n        p = p[1:-1].upper()\n\n    if \"-\" in p and p != \"-\":\n        p = p.replace(\"-\", \"_\")\n\n    if p.endswith(\"?\") and p != \"?\":\n        p = \"is_%s\" % (p[:-1])\n\n    if p.endswith(\"!\") and p != \"!\":\n        p = \"%s_bang\" % (p[:-1])\n\n    return p\n\n\ndef hy_symbol_unmangle(p):\n    # hy_symbol_mangle is one-way, so this can't be perfect.\n    # But it can be useful till we have a way to get the original\n    # symbol (https://github.com/hylang/hy/issues/360).\n    p = str_type(p)\n\n    if p.endswith(\"_bang\") and p != \"_bang\":\n        p = p[:-len(\"_bang\")] + \"!\"\n\n    if p.startswith(\"is_\") and p != \"is_\":\n        p = p[len(\"is_\"):] + \"?\"\n\n    if \"_\" in p and p != \"_\":\n        p = p.replace(\"_\", \"-\")\n\n    if (all([c.isalpha() and c.isupper() or c == '_' for c in p]) and\n            any([c.isalpha() for c in p])):\n        p = '*' + p.lower() + '*'\n\n    return p\n\n\ndef set_boundaries(fun):\n    @wraps(fun)\n    def wrapped(p):\n        start = p[0].source_pos\n        end = p[-1].source_pos\n        ret = fun(p)\n        ret.start_line = start.lineno\n        ret.start_column = start.colno\n        if start is not end:\n            ret.end_line = end.lineno\n            ret.end_column = end.colno\n        else:\n            ret.end_line = start.lineno\n            ret.end_column = start.colno + len(p[0].value)\n        return ret\n    return wrapped\n\n\ndef set_quote_boundaries(fun):\n    @wraps(fun)\n    def wrapped(p):\n        start = p[0].source_pos\n        ret = fun(p)\n        ret.start_line = start.lineno\n        ret.start_column = start.colno\n        ret.end_line = p[-1].end_line\n        ret.end_column = p[-1].end_column\n        return ret\n    return wrapped\n\n\n@pg.production(\"main : HASHBANG real_main\")\ndef main_hashbang(p):\n    return p[1]\n\n\n@pg.production(\"main : real_main\")\ndef main(p):\n    return p[0]\n\n\n@pg.production(\"real_main : list_contents\")\ndef real_main(p):\n    return p[0]\n\n\n@pg.production(\"real_main : $end\")\ndef real_main_empty(p):\n    return []\n\n\ndef reject_spurious_dots(*items):\n    \"Reject the spurious dots from items\"\n    for list in items:\n        for tok in list:\n            if tok == \".\" and type(tok) == HySymbol:\n                raise LexException(\"Malformed dotted list\",\n                                   tok.start_line, tok.start_column)\n\n\n@pg.production(\"paren : LPAREN list_contents RPAREN\")\n@set_boundaries\ndef paren(p):\n    cont = p[1]\n\n    # Dotted lists are expressions of the form\n    # (a b c . d)\n    # that evaluate to nested cons cells of the form\n    # (a . (b . (c . d)))\n    if len(cont) >= 3 and isinstance(cont[-2], HySymbol) and cont[-2] == \".\":\n\n        reject_spurious_dots(cont[:-2], cont[-1:])\n\n        if len(cont) == 3:\n            # Two-item dotted list: return the cons cell directly\n            return HyCons(cont[0], cont[2])\n        else:\n            # Return a nested cons cell\n            return HyCons(cont[0], paren([p[0], cont[1:], p[2]]))\n\n    # Warn preemptively on a malformed dotted list.\n    # Only check for dots after the first item to allow for a potential\n    # attribute accessor shorthand\n    reject_spurious_dots(cont[1:])\n\n    return HyExpression(p[1])\n\n\n@pg.production(\"paren : LPAREN RPAREN\")\n@set_boundaries\ndef empty_paren(p):\n    return HyExpression([])\n\n\n@pg.production(\"list_contents : term list_contents\")\ndef list_contents(p):\n    return [p[0]] + p[1]\n\n\n@pg.production(\"list_contents : term\")\ndef list_contents_single(p):\n    return [p[0]]\n\n\n@pg.production(\"term : identifier\")\n@pg.production(\"term : paren\")\n@pg.production(\"term : dict\")\n@pg.production(\"term : list\")\n@pg.production(\"term : set\")\n@pg.production(\"term : string\")\ndef term(p):\n    return p[0]\n\n\n@pg.production(\"term : QUOTE term\")\n@set_quote_boundaries\ndef term_quote(p):\n    return HyExpression([HySymbol(\"quote\"), p[1]])\n\n\n@pg.production(\"term : QUASIQUOTE term\")\n@set_quote_boundaries\ndef term_quasiquote(p):\n    return HyExpression([HySymbol(\"quasiquote\"), p[1]])\n\n\n@pg.production(\"term : UNQUOTE term\")\n@set_quote_boundaries\ndef term_unquote(p):\n    return HyExpression([HySymbol(\"unquote\"), p[1]])\n\n\n@pg.production(\"term : UNQUOTESPLICE term\")\n@set_quote_boundaries\ndef term_unquote_splice(p):\n    return HyExpression([HySymbol(\"unquote_splice\"), p[1]])\n\n\n@pg.production(\"term : HASHOTHER term\")\n@set_quote_boundaries\ndef hash_other(p):\n    # p == [(Token('HASHOTHER', '#foo'), bar)]\n    st = p[0].getstr()[1:]\n    str_object = HyString(st)\n    expr = p[1]\n    return HyExpression([HySymbol(\"dispatch_tag_macro\"), str_object, expr])\n\n\n@pg.production(\"set : HLCURLY list_contents RCURLY\")\n@set_boundaries\ndef t_set(p):\n    return HySet(p[1])\n\n\n@pg.production(\"set : HLCURLY RCURLY\")\n@set_boundaries\ndef empty_set(p):\n    return HySet([])\n\n\n@pg.production(\"dict : LCURLY list_contents RCURLY\")\n@set_boundaries\ndef t_dict(p):\n    return HyDict(p[1])\n\n\n@pg.production(\"dict : LCURLY RCURLY\")\n@set_boundaries\ndef empty_dict(p):\n    return HyDict([])\n\n\n@pg.production(\"list : LBRACKET list_contents RBRACKET\")\n@set_boundaries\ndef t_list(p):\n    return HyList(p[1])\n\n\n@pg.production(\"list : LBRACKET RBRACKET\")\n@set_boundaries\ndef t_empty_list(p):\n    return HyList([])\n\n\nif PY3:\n    def uni_hystring(s):\n        return HyString(literal_eval(s))\n\n    def hybytes(s):\n        return HyBytes(literal_eval('b'+s))\n\nelse:\n    def uni_hystring(s):\n        return HyString(literal_eval('u'+s))\n\n    def hybytes(s):\n        return HyBytes(literal_eval(s))\n\n\n@pg.production(\"string : STRING\")\n@set_boundaries\ndef t_string(p):\n    # remove trailing quote\n    s = p[0].value[:-1]\n    # get the header\n    header, s = s.split('\"', 1)\n    # remove unicode marker (this is redundant because Hy string\n    # literals already, by default, generate Unicode literals\n    # under Python 2)\n    header = header.replace(\"u\", \"\")\n    # remove bytes marker, since we'll need to exclude it for Python 2\n    is_bytestring = \"b\" in header\n    header = header.replace(\"b\", \"\")\n    # build python string\n    s = header + '\"\"\"' + s + '\"\"\"'\n    return (hybytes if is_bytestring else uni_hystring)(s)\n\n\n@pg.production(\"string : PARTIAL_STRING\")\ndef t_partial_string(p):\n    # Any unterminated string requires more input\n    raise PrematureEndOfInput(\"Premature end of input\")\n\n\n@pg.production(\"identifier : IDENTIFIER\")\n@set_boundaries\ndef t_identifier(p):\n    obj = p[0].value\n\n    val = symbol_like(obj)\n    if val is not None:\n        return val\n\n    if \".\" in obj and symbol_like(obj.split(\".\", 1)[0]) is not None:\n        # E.g., `5.attr` or `:foo.attr`\n        raise LexException(\n            'Cannot access attribute on anything other than a name (in '\n            'order to get attributes of expressions, use '\n            '`(. <expression> <attr>)` or `(.<attr> <expression>)`)',\n            p[0].source_pos.lineno, p[0].source_pos.colno)\n\n    return HySymbol(\".\".join(hy_symbol_mangle(x) for x in obj.split(\".\")))\n\n\ndef symbol_like(obj):\n    \"Try to interpret `obj` as a number or keyword.\"\n\n    try:\n        return HyInteger(obj)\n    except ValueError:\n        pass\n\n    if '/' in obj:\n        try:\n            lhs, rhs = obj.split('/')\n            return HyExpression([HySymbol('fraction'), HyInteger(lhs),\n                                 HyInteger(rhs)])\n        except ValueError:\n            pass\n\n    try:\n        return HyFloat(obj)\n    except ValueError:\n        pass\n\n    if obj != 'j':\n        try:\n            return HyComplex(obj)\n        except ValueError:\n            pass\n\n    if obj.startswith(\":\") and \".\" not in obj:\n        return HyKeyword(obj)\n\n\n@pg.error\ndef error_handler(token):\n    tokentype = token.gettokentype()\n    if tokentype == '$end':\n        raise PrematureEndOfInput(\"Premature end of input\")\n    else:\n        raise LexException(\n            \"Ran into a %s where it wasn't expected.\" % tokentype,\n            token.source_pos.lineno, token.source_pos.colno)\n\n\nparser = pg.build()\n", "path": "hy/lex/parser.py"}]}