defclass reimagined
defclass now has a new syntax: (defclass Name [BaseList] [property value property value] ;; optional (defn method [self] self.property)) Anything after the optional property list (which will be translated to a setv within the class context) will be added to the class body. This allows one to have side effects and complex expressions within the class definition. As a side effect, defining methods is much more friendly now! Closes #850. Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
This commit is contained in:
parent
9fbd21adba
commit
cbc2eed900
@ -357,7 +357,9 @@ attributes of the new class as two item vectors.
|
||||
.. code-block:: clj
|
||||
|
||||
(defclass class-name [super-class-1 super-class-2]
|
||||
[[attribute value]])
|
||||
[attribute value]
|
||||
|
||||
(defn method [self] (print "hello!")))
|
||||
|
||||
Both values and functions can be bound on the new class as shown by the example
|
||||
below:
|
||||
@ -365,9 +367,10 @@ below:
|
||||
.. code-block:: clj
|
||||
|
||||
=> (defclass Cat []
|
||||
... [[age None]
|
||||
... [colour "white"]
|
||||
... [speak (fn [self] (print "Meow"))]])
|
||||
... [age None
|
||||
... colour "white"]
|
||||
...
|
||||
... (defn speak [self] (print "Meow")))
|
||||
|
||||
=> (def spot (Cat))
|
||||
=> (setv spot.colour "Black")
|
||||
|
@ -477,17 +477,14 @@ In Hy:
|
||||
|
||||
(defclass FooBar [object]
|
||||
"Yet Another Example Class"
|
||||
[[--init--
|
||||
(fn [self x]
|
||||
(setv self.x x)
|
||||
; Currently needed for --init-- because __init__ needs None
|
||||
; Hopefully this will go away :)
|
||||
None)]
|
||||
|
||||
[get-x
|
||||
(fn [self]
|
||||
"Return our copy of x"
|
||||
self.x)]])
|
||||
(defn --init-- [self x]
|
||||
(setv self.x x)
|
||||
None)
|
||||
|
||||
(defn get-x [self]
|
||||
"Return our copy of x"
|
||||
self.x))
|
||||
|
||||
|
||||
You can also do class-level attributes. In Python::
|
||||
@ -502,9 +499,9 @@ In Hy:
|
||||
.. code-block:: clj
|
||||
|
||||
(defclass Customer [models.Model]
|
||||
[[name (models.CharField :max-length 255})]
|
||||
[address (models.TextField)]
|
||||
[notes (models.TextField)]])
|
||||
[name (models.CharField :max-length 255})
|
||||
address (models.TextField)
|
||||
notes (models.TextField)])
|
||||
|
||||
Hy <-> Python interop
|
||||
=====================
|
||||
|
@ -2238,15 +2238,15 @@ class HyASTCompiler(object):
|
||||
|
||||
@builds("defclass")
|
||||
@checkargs(min=1)
|
||||
def compile_class_expression(self, expression):
|
||||
expression.pop(0) # class
|
||||
def compile_class_expression(self, expressions):
|
||||
expressions.pop(0) # class
|
||||
|
||||
class_name = expression.pop(0)
|
||||
class_name = expressions.pop(0)
|
||||
|
||||
if expression:
|
||||
base_list = expression.pop(0)
|
||||
if expressions:
|
||||
base_list = expressions.pop(0)
|
||||
if not isinstance(base_list, HyList):
|
||||
raise HyTypeError(expression,
|
||||
raise HyTypeError(expressions,
|
||||
"Bases class must be a list")
|
||||
bases_expr, bases, _ = self._compile_collect(base_list)
|
||||
else:
|
||||
@ -2256,8 +2256,8 @@ class HyASTCompiler(object):
|
||||
body = Result()
|
||||
|
||||
# grab the doc string, if there is one
|
||||
if expression and isinstance(expression[0], HyString):
|
||||
docstring = expression.pop(0)
|
||||
if expressions and isinstance(expressions[0], HyString):
|
||||
docstring = expressions.pop(0)
|
||||
symb = HySymbol("__doc__")
|
||||
symb.start_line = docstring.start_line
|
||||
symb.start_column = docstring.start_column
|
||||
@ -2266,31 +2266,25 @@ class HyASTCompiler(object):
|
||||
docstring.start_column)
|
||||
body += body.expr_as_stmt()
|
||||
|
||||
if expression:
|
||||
try:
|
||||
body_expression = iter(expression.pop(0))
|
||||
except TypeError:
|
||||
raise HyTypeError(
|
||||
expression,
|
||||
"Wrong argument type for defclass attributes definition.")
|
||||
for b in body_expression:
|
||||
if isinstance(b, HyExpression):
|
||||
b = macroexpand(b, self.module_name)
|
||||
if len(b) != 2:
|
||||
raise HyTypeError(
|
||||
expression,
|
||||
"Wrong number of argument in defclass attribute.")
|
||||
body += self._compile_assign(b[0], b[1],
|
||||
b.start_line, b.start_column)
|
||||
body += body.expr_as_stmt()
|
||||
if expressions and isinstance(expressions[0], HyList) \
|
||||
and not isinstance(expressions[0], HyExpression):
|
||||
expr = expressions.pop(0)
|
||||
body += self.compile(
|
||||
HyExpression([
|
||||
HySymbol("setv")
|
||||
] + expr).replace(expr)
|
||||
)
|
||||
|
||||
for expression in expressions:
|
||||
body += self.compile(macroexpand(expression, self.module_name))
|
||||
|
||||
if not body.stmts:
|
||||
body += ast.Pass(lineno=expression.start_line,
|
||||
col_offset=expression.start_column)
|
||||
body += ast.Pass(lineno=expressions.start_line,
|
||||
col_offset=expressions.start_column)
|
||||
|
||||
return bases + ast.ClassDef(
|
||||
lineno=expression.start_line,
|
||||
col_offset=expression.start_column,
|
||||
lineno=expressions.start_line,
|
||||
col_offset=expressions.start_column,
|
||||
decorator_list=[],
|
||||
name=ast_str(class_name),
|
||||
keywords=[],
|
||||
|
@ -2,12 +2,11 @@
|
||||
|
||||
(defclass FakeMeth []
|
||||
"Mocking decorator class"
|
||||
[[rules {}]
|
||||
[route (fn [self rule &kwargs options]
|
||||
(fn [f]
|
||||
(assoc self.rules rule (, f options))
|
||||
f))]])
|
||||
|
||||
[rules {}]
|
||||
(defn route [self rule &kwargs options]
|
||||
(fn [f]
|
||||
(assoc self.rules rule (, f options))
|
||||
f)))
|
||||
|
||||
(defn test_route []
|
||||
(let [[app (FakeMeth)]]
|
||||
|
@ -23,7 +23,7 @@
|
||||
(defn test-defclass-attrs []
|
||||
"NATIVE: test defclass attributes"
|
||||
(defclass A []
|
||||
[[x 42]])
|
||||
[x 42])
|
||||
(assert (= A.x 42))
|
||||
(assert (= (getattr (A) "x") 42)))
|
||||
|
||||
@ -31,9 +31,9 @@
|
||||
(defn test-defclass-attrs-fn []
|
||||
"NATIVE: test defclass attributes with fn"
|
||||
(defclass B []
|
||||
[[x 42]
|
||||
[y (fn [self value]
|
||||
(+ self.x value))]])
|
||||
[x 42
|
||||
y (fn [self value]
|
||||
(+ self.x value))])
|
||||
(assert (= B.x 42))
|
||||
(assert (= (.y (B) 5) 47))
|
||||
(let [[b (B)]]
|
||||
@ -44,17 +44,17 @@
|
||||
(defn test-defclass-dynamic-inheritance []
|
||||
"NATIVE: test defclass with dynamic inheritance"
|
||||
(defclass A [((fn [] (if true list dict)))]
|
||||
[[x 42]])
|
||||
[x 42])
|
||||
(assert (isinstance (A) list))
|
||||
(defclass A [((fn [] (if false list dict)))]
|
||||
[[x 42]])
|
||||
[x 42])
|
||||
(assert (isinstance (A) dict)))
|
||||
|
||||
|
||||
(defn test-defclass-no-fn-leak []
|
||||
"NATIVE: test defclass attributes with fn"
|
||||
(defclass A []
|
||||
[[x (fn [] 1)]])
|
||||
[x (fn [] 1)])
|
||||
(try
|
||||
(do
|
||||
(x)
|
||||
@ -64,13 +64,13 @@
|
||||
(defn test-defclass-docstring []
|
||||
"NATIVE: test defclass docstring"
|
||||
(defclass A []
|
||||
[[--doc-- "doc string"]
|
||||
[x 1]])
|
||||
[--doc-- "doc string"
|
||||
x 1])
|
||||
(setv a (A))
|
||||
(assert (= a.__doc__ "doc string"))
|
||||
(defclass B []
|
||||
"doc string"
|
||||
[[x 1]])
|
||||
[x 1])
|
||||
(setv b (B))
|
||||
(assert (= b.x 1))
|
||||
(assert (= b.__doc__ "doc string"))
|
||||
@ -78,7 +78,7 @@
|
||||
"begin a very long multi-line string to make
|
||||
sure that it comes out the way we hope
|
||||
and can span 3 lines end."
|
||||
[[x 1]])
|
||||
[x 1])
|
||||
(setv mL (MultiLine))
|
||||
(assert (= mL.x 1))
|
||||
(assert (in "begin" mL.__doc__))
|
||||
@ -86,8 +86,26 @@
|
||||
|
||||
(defn test-defclass-macroexpand []
|
||||
"NATIVE: test defclass with macro expand"
|
||||
(defmacro M [] `[x (fn [self x] (setv self._x x))])
|
||||
(defclass A [] [(M)])
|
||||
(defmacro M [] `(defn x [self x] (setv self._x x)))
|
||||
(defclass A [] (M))
|
||||
(setv a (A))
|
||||
(a.x 1)
|
||||
(assert (= a._x 1)))
|
||||
|
||||
(defn test-defclass-syntax []
|
||||
"NATIVE: test defclass syntax with properties and methods and side-effects"
|
||||
(setv foo 1)
|
||||
(defclass A []
|
||||
[x 1
|
||||
y 2]
|
||||
(global foo)
|
||||
(setv foo 2)
|
||||
(defn greet [self]
|
||||
"Greet the caller"
|
||||
|
||||
"hello!"))
|
||||
(setv a (A))
|
||||
(assert (= a.x 1))
|
||||
(assert (= a.y 2))
|
||||
(assert foo 2)
|
||||
(assert (.greet a) "hello"))
|
||||
|
@ -145,21 +145,21 @@
|
||||
|
||||
|
||||
(defclass HyTestMatrix [list]
|
||||
[[--matmul--
|
||||
(fn [self other]
|
||||
(let [[n (len self)]
|
||||
[m (len (. other [0]))]
|
||||
[result []]]
|
||||
(for [i (range m)]
|
||||
(let [[result-row []]]
|
||||
(for [j (range n)]
|
||||
(let [[dot-product 0]]
|
||||
(for [k (range (len (. self [0])))]
|
||||
(+= dot-product (* (. self [i] [k])
|
||||
(. other [k] [j]))))
|
||||
(.append result-row dot-product)))
|
||||
(.append result result-row)))
|
||||
result))]])
|
||||
[--matmul--
|
||||
(fn [self other]
|
||||
(let [[n (len self)]
|
||||
[m (len (. other [0]))]
|
||||
[result []]]
|
||||
(for [i (range m)]
|
||||
(let [[result-row []]]
|
||||
(for [j (range n)]
|
||||
(let [[dot-product 0]]
|
||||
(for [k (range (len (. self [0])))]
|
||||
(+= dot-product (* (. self [i] [k])
|
||||
(. other [k] [j]))))
|
||||
(.append result-row dot-product)))
|
||||
(.append result result-row)))
|
||||
result))])
|
||||
|
||||
(def first-test-matrix (HyTestMatrix [[1 2 3]
|
||||
[4 5 6]
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
(with-decorator bardec
|
||||
(defclass cls []
|
||||
[[my_attr 456]]))
|
||||
[my_attr 456]))
|
||||
|
||||
(defn test-decorator-clobbing []
|
||||
"NATIVE: Tests whether nested decorators work"
|
||||
|
@ -1,16 +1,13 @@
|
||||
(defclass WithTest [object]
|
||||
[(--init--
|
||||
(fn [self val]
|
||||
(setv self.val val)
|
||||
None))
|
||||
(defn --init-- [self val]
|
||||
(setv self.val val)
|
||||
None)
|
||||
|
||||
(--enter--
|
||||
(fn [self]
|
||||
self.val))
|
||||
(defn --enter-- [self]
|
||||
self.val)
|
||||
|
||||
(--exit--
|
||||
(fn [self type value traceback]
|
||||
(setv self.val None)))])
|
||||
(defn --exit-- [self type value traceback]
|
||||
(setv self.val None)))
|
||||
|
||||
(defn test-single-with []
|
||||
"NATIVE: test a single with"
|
||||
|
Loading…
Reference in New Issue
Block a user