In order to fix #608, we need to know which symbols can't be assigned.

Python has the keyword.iskeyword method we can leverage for Python
keywords, but we also need to address Hy builtins like 'get' or
'slice'.

And to make behavior compatible with Python 2 or 3, we also make
a special case to prevent assignment to False, True or None as
well as the Hy versions: false, true, null, and nil.

For non-Hy modules, we also check to make sure the symbol is not
part of the compiler. This allows shadow.hy to override "+" but
prevents general use from re-defn-ing 'get' or 'do'.
This commit is contained in:
Bob Tolbert 2014-06-29 11:19:22 -06:00
parent f3ecb96119
commit 16e908d56e
2 changed files with 44 additions and 0 deletions

View File

@ -47,6 +47,7 @@ import importlib
import codecs
import ast
import sys
import keyword
from collections import defaultdict
@ -73,6 +74,21 @@ def load_stdlib():
_stdlib[e] = module
# True, False and None included here since they
# are assignable in Python 2.* but become
# keywords in Python 3.*
def _is_hy_builtin(name, module_name):
extras = ['True', 'False', 'None',
'true', 'false', 'nil', 'null']
if name in extras or keyword.iskeyword(name):
return True
# for non-Hy modules, check for pre-existing name in
# _compile_table
if not module_name.startswith("hy."):
return name in _compile_table
return False
_compile_table = {}
@ -1751,6 +1767,12 @@ class HyASTCompiler(object):
def _compile_assign(self, name, result,
start_line, start_column):
str_name = "%s" % name
if _is_hy_builtin(str_name, self.module_name):
raise HyTypeError(name,
"Can't assign to a builtin: `%s'" % str_name)
result = self.compile(result)
ld_name = self.compile(name)

View File

@ -28,6 +28,28 @@
(setv (get foo 0) 12)
(assert (= (get foo 0) 12)))
(defn test-setv-builtin []
"NATIVE: test that setv doesn't work on builtins"
(try (eval '(setv False 1))
(catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e)))))
(try (eval '(setv True 0))
(catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e)))))
(try (eval '(setv None 1))
(catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e)))))
(try (eval '(setv false 1))
(catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e)))))
(try (eval '(setv true 0))
(catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e)))))
(try (eval '(setv nil 1))
(catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e)))))
(try (eval '(setv null 1))
(catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e)))))
(try (eval '(defn defclass [] (print "hello")))
(catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e)))))
(try (eval '(defn get [] (print "hello")))
(catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e)))))
(try (eval '(defn lambda [] (print "hello")))
(catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))))
(defn test-for-loop []
"NATIVE: test for loops"