diff --git a/hy/core/language.hy b/hy/core/language.hy index 749fa9e..bb29b10 100644 --- a/hy/core/language.hy +++ b/hy/core/language.hy @@ -28,10 +28,10 @@ (import collections) (import sys) (import [hy._compat [long-type]]) ; long for python2, int for python3 -(import [hy.models.cons [HyCons]]) +(import [hy.models.cons [HyCons]] + [hy.models.keyword [HyKeyword *keyword-prefix*]]) (import [hy.lex [LexException PrematureEndOfInput tokenize]]) - (defn _numeric-check [x] (if (not (numeric? x)) (raise (TypeError (.format "{0!r} is not a number" x))))) @@ -370,13 +370,45 @@ (defun Botsbuildbots () (Botsbuildbots)) +(defn zipwith [func &rest lists] + "Zip the contents of several lists and map a function to the result" + (do + (import functools) + (map (functools.partial (fn [f args] (apply f args)) func) (apply zip lists)))) + +(defn hyify [text] + "Convert text to match hy identifier" + (.replace (string text) "_" "-")) + +(defn keyword [value] + "Create a keyword from the given value. Strings numbers and even objects + with the __name__ magic will work" + (if (and (string? value) (value.startswith *keyword-prefix*)) + (hyify value) + (if (string? value) + (HyKeyword (+ ":" (hyify value))) + (try + (hyify (.__name__ value)) + (catch [] (HyKeyword (+ ":" (string value)))))))) + +(defn name [value] + "Convert the given value to a string. Keyword special character will be stripped. + String will be used as is. Even objects with the __name__ magic will work" + (if (and (string? value) (value.startswith *keyword-prefix*)) + (hyify (slice value 2)) + (if (string? value) + (hyify value) + (try + (hyify (. value __name__)) + (catch [] (string value)))))) + (def *exports* '[Botsbuildbots butlast calling-module-name coll? cons cons? cycle dec distinct disassemble drop drop-while empty? even? every? first filter filterfalse flatten float? gensym identity inc input instance? integer integer? integer-char? interleave - interpose iterable? iterate iterator? keyword? list* - macroexpand macroexpand-1 map merge-with neg? nil? none? nth - numeric? odd? pos? range read remove repeat repeatedly + interpose iterable? iterate iterator? keyword keyword? list* + macroexpand macroexpand-1 map merge-with name neg? nil? none? + nth numeric? odd? pos? range read remove repeat repeatedly rest reduce second some string string? take take-nth take-while zero? zip zip_longest zipwith]) diff --git a/hy/models/keyword.py b/hy/models/keyword.py index 6d25633..32b2306 100644 --- a/hy/models/keyword.py +++ b/hy/models/keyword.py @@ -23,14 +23,17 @@ from hy.models import HyObject from hy._compat import str_type +KEYWORD_PREFIX = "\uFDD0" + + 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): - if not value.startswith("\uFDD0"): - value = "\uFDD0" + value + if not value.startswith(KEYWORD_PREFIX): + value = KEYWORD_PREFIX + value obj = str_type.__new__(cls, value) return obj diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 895249f..4c73455 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -1056,11 +1056,11 @@ (import [StringIO [StringIO]]) (import [io [StringIO]])) (import [hy.models.expression [HyExpression]]) - + (def stdin-buffer (StringIO "(+ 2 2)\n(- 2 2)")) (assert (= (eval (read stdin-buffer)) 4)) (assert (isinstance (read stdin-buffer) HyExpression)) - + "Multiline test" (def stdin-buffer (StringIO "(\n+\n41\n1\n)\n(-\n2\n1\n)")) (assert (= (eval (read stdin-buffer)) 42)) @@ -1069,9 +1069,33 @@ "EOF test" (def stdin-buffer (StringIO "(+ 2 2)")) (read stdin-buffer) - (try + (try (read stdin-buffer) (catch [e Exception] (assert (isinstance e EOFError))))) +(defn test-keyword-creation [] + "NATIVE: Test keyword creation" + (assert (= (keyword "foo") :foo)) + (assert (= (keyword "foo_bar") :foo-bar)) + (assert (= (keyword `foo) :foo)) + (assert (= (keyword `foo-bar) :foo-bar)) + (assert (= (keyword 'foo) :foo)) + (assert (= (keyword 'foo-bar) :foo-bar)) + (assert (= (keyword 1) :1)) + (assert (= (keyword 1.0) :1.0)) + (assert (= (keyword :foo_bar) :foo-bar))) +(defn test-name-conversion [] + "NATIVE: Test name conversion" + (assert (= (name "foo") "foo")) + (assert (= (name "foo_bar") "foo-bar")) + (assert (= (name `foo) "foo")) + (assert (= (name `foo_bar) "foo-bar")) + (assert (= (name 'foo) "foo")) + (assert (= (name 'foo_bar) "foo-bar")) + (assert (= (name 1) "1")) + (assert (= (name 1.0) "1.0")) + (assert (= (name :foo) "foo")) + (assert (= (name :foo_bar) "foo-bar")) + (assert (= (name test-name-conversion) "test-name-conversion")))