diff --git a/hy/compiler.py b/hy/compiler.py index 9b1c337..a8bee1a 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -29,6 +29,7 @@ from hy.models.string import HyString from hy.models.symbol import HySymbol from hy.models.list import HyList from hy.models.dict import HyDict +from hy.models.keyword import HyKeyword from hy.util import flatten_literal_list @@ -768,6 +769,11 @@ class HyASTCompiler(object): if expression[0].startswith("."): return self.compile_dotted_expression(expression) + if isinstance(fn, HyKeyword): + new_expr = HyExpression(["get", expression[1], fn]) + new_expr.start_line = expression.start_line + new_expr.start_column = expression.start_column + return self.compile_index_expression(new_expr) return ast.Call(func=self.compile(fn), args=[self.compile(x) for x in expression[1:]], @@ -927,6 +933,14 @@ class HyASTCompiler(object): return ast.Str(s=ast_str(string), lineno=string.start_line, col_offset=string.start_column) + @builds(HyKeyword) + def compile_keyword(self, keyword): + _str_type = str + if sys.version_info[0] < 3: + _str_type = unicode + return ast.Str(s=_str_type(keyword), lineno=keyword.start_line, + col_offset=keyword.start_column) + @builds(HyDict) def compile_dict(self, m): keys = [] diff --git a/hy/lex/states.py b/hy/lex/states.py index f6ec6eb..de83f56 100644 --- a/hy/lex/states.py +++ b/hy/lex/states.py @@ -22,6 +22,7 @@ from hy.models.expression import HyExpression from hy.models.integer import HyInteger from hy.models.symbol import HySymbol from hy.models.string import HyString +from hy.models.keyword import HyKeyword from hy.models.dict import HyDict from hy.models.list import HyList @@ -61,6 +62,9 @@ def _resolve_atom(obj): if obj in table: return HySymbol(table[obj]) + if obj.startswith(":"): + return HyKeyword(obj) + if obj.startswith("*") and obj.endswith("*") and obj != "*": obj = obj[1:-1].upper() diff --git a/hy/models/keyword.py b/hy/models/keyword.py new file mode 100644 index 0000000..e57d2f1 --- /dev/null +++ b/hy/models/keyword.py @@ -0,0 +1,39 @@ +# Copyright (c) 2013 Gergely Nagy +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +from __future__ import unicode_literals +from hy.models import HyObject +import sys + + +if sys.version_info[0] >= 3: + _str_type = str +else: + _str_type = unicode + + +class HyKeyword(HyObject, _str_type): + """Generic Hy Keyword object. It's either a ``str`` or a ``unicode``, + depending on the Python version. + """ + + def __new__(cls, value): + obj = _str_type.__new__(cls, "\uFDD0" + value) + return obj diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 0bd08ff..45045c6 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -510,6 +510,18 @@ (pass) ((fn [] 1)))))) +(defn test-keyword [] + "NATIVE: test if keywords are recognised" + + (assert (= :foo :foo)) + (assert (= (get {:foo "bar"} :foo) "bar")) + (assert (= (get {:bar "quux"} (get {:foo :bar} :foo)) "quux"))) + +(defn test-keyword-clash [] + "NATIVE: test that keywords do not clash with normal strings" + + (assert (= (get {:foo "bar" ":foo" "quux"} :foo) "bar")) + (assert (= (get {:foo "bar" ":foo" "quux"} ":foo") "quux"))) (defn test-nested-if [] "NATIVE: test nested if"