Add a module for getting reserved words (#1171)

This is helpful for writing syntax highlighters (e.g., https://github.com/hylang/hy/pull/1170).
This commit is contained in:
Kodi Arfer 2016-12-12 00:11:42 -08:00 committed by Tuukka Turto
parent b3d7069fb3
commit 5b85990d87
7 changed files with 119 additions and 0 deletions

View File

@ -72,3 +72,4 @@
* Jakub Wilk <jwilk@jwilk.net>
* Kodi Arfer <git@arfer.net>
* Karan Sharma <karansharma1295@gmail.com>
* Sergey Sobko <s.sobko@profitware.ru>

View File

@ -1261,3 +1261,22 @@ Returns an iterator from *coll* as long as *pred* returns ``True``.
=> (list (take-while neg? [ 1 2 3 -4 5]))
[]
Other Built-Ins
===============
hy.core.reserved
----------------
Usage: ``(hy.core.reserved.names)``
This module can be used to get a list (actually, a ``frozenset``) of the
names of Hy's built-in functions, macros, and special forms. The output
also includes all Python reserved words. All names are in unmangled form
(e.g., ``list-comp`` rather than ``list_comp``).
.. code-block:: hy
=> (import hy)
=> (in "defclass" (hy.core.reserved.names))
True

View File

@ -1,3 +1,5 @@
from . import reserved # noqa
STDLIB = [
"hy.core.language",
"hy.core.shadow"

41
hy/core/reserved.hy Normal file
View File

@ -0,0 +1,41 @@
;;; Get a frozenset of Hy reserved words
;;
;; Copyright (c) 2016 Paul Tagliamonte <paultag@debian.org>
;;
;; 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.
(import hy sys keyword)
(setv _cache None)
(defn names []
"Return a frozenset of reserved symbol names.
The result of the first call is cached."
(global _cache)
(if (is _cache None) (do
(setv unmangle (. sys.modules ["hy.lex.parser"] hy_symbol_unmangle))
(setv _cache (frozenset (map unmangle (+
hy.core.language.*exports*
hy.core.shadow.*exports*
(list (.keys (get hy.macros._hy_macros None)))
keyword.kwlist
(list-comp k [k (.keys hy.compiler.-compile-table)]
(isinstance k hy._compat.string-types))))))))
_cache)

View File

@ -61,6 +61,30 @@ def hy_symbol_mangle(p):
return p
def hy_symbol_unmangle(p):
# hy_symbol_mangle is one-way, so this can't be perfect.
# But it can be useful till we have a way to get the original
# symbol (https://github.com/hylang/hy/issues/360).
from hy._compat import str_type
p = str_type(p)
if p.endswith("_bang") and p != "_bang":
p = p[:-len("_bang")] + "!"
if p.startswith("is_") and p != "is_":
p = p[len("is_"):] + "?"
if "_" in p and p != "_":
p = p.replace("_", "-")
if (all([c.isalpha() and c.isupper() or c == '_' for c in p]) and
any([c.isalpha() for c in p])):
p = '*' + p.lower() + '*'
return p
def set_boundaries(fun):
@wraps(fun)
def wrapped(p):

View File

@ -360,6 +360,24 @@ def test_lex_mangling_bang():
assert entry == [HySymbol(".foo_bang.bar.baz_bang")]
def test_unmangle():
import sys
f = sys.modules["hy.lex.parser"].hy_symbol_unmangle
assert f("FOO") == "*foo*"
assert f("<") == "<"
assert f("FOOa") == "FOOa"
assert f("foo_bar") == "foo-bar"
assert f("_") == "_"
assert f("is_foo") == "foo?"
assert f("is_") == "is-"
assert f("foo_bang") == "foo!"
assert f("_bang") == "-bang"
def test_simple_cons():
"""Check that cons gets tokenized correctly"""
entry = tokenize("(a . b)")[0]

View File

@ -612,3 +612,17 @@
[1 3 6 10 15])
(assert-equal (list (accumulate [1 -2 -3 -4 -5] -))
[1 3 6 10 15]))
(defn test-reserved []
(import [hy.core.reserved [names]])
(assert (is (type (names)) frozenset))
(assert (in "and" (names)))
(when PY3
(assert (in "False" (names))))
(assert (in "pass" (names)))
(assert (in "class" (names)))
(assert (in "defclass" (names)))
(assert (in "->" (names)))
(assert (in "keyword?" (names)))
(assert (not-in "foo" (names)))
(assert (not-in "hy" (names))))