From 702811c69aaf3267ee274ced3b86f5d27323b0fb Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Wed, 10 Apr 2013 17:44:08 +0200 Subject: [PATCH 1/2] Implement :keyword support This implements keywords, ":" prefixed symbols that are able to look themselves up in a collection. They're internally stored as strings that start with "\ufdd0". This fixes #22. Signed-off-by: Gergely Nagy --- hy/compiler.py | 14 ++++++++++++ hy/lex/states.py | 4 ++++ hy/models/keyword.py | 39 ++++++++++++++++++++++++++++++++++ tests/native_tests/language.hy | 6 ++++++ 4 files changed, 63 insertions(+) create mode 100644 hy/models/keyword.py 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 52d6fa3..fa39176 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 018cbc3..ab71791 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -510,6 +510,12 @@ (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-eval [] "NATIVE: test eval" From 2ea2cd1a8dc74c7570df8d01e792b018401bc7fd Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Thu, 11 Apr 2013 11:46:52 +0200 Subject: [PATCH 2/2] tests: Test that keywords do not clash with normal strings Signed-off-by: Gergely Nagy --- tests/native_tests/language.hy | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index ab71791..f4c0e1c 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -517,6 +517,12 @@ (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-eval [] "NATIVE: test eval" (assert (= 2 (eval (quote (+ 1 1)))))