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
|
.. code-block:: clj
|
||||||
|
|
||||||
(defclass class-name [super-class-1 super-class-2]
|
(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
|
Both values and functions can be bound on the new class as shown by the example
|
||||||
below:
|
below:
|
||||||
@ -365,9 +367,10 @@ below:
|
|||||||
.. code-block:: clj
|
.. code-block:: clj
|
||||||
|
|
||||||
=> (defclass Cat []
|
=> (defclass Cat []
|
||||||
... [[age None]
|
... [age None
|
||||||
... [colour "white"]
|
... colour "white"]
|
||||||
... [speak (fn [self] (print "Meow"))]])
|
...
|
||||||
|
... (defn speak [self] (print "Meow")))
|
||||||
|
|
||||||
=> (def spot (Cat))
|
=> (def spot (Cat))
|
||||||
=> (setv spot.colour "Black")
|
=> (setv spot.colour "Black")
|
||||||
|
@ -477,17 +477,14 @@ In Hy:
|
|||||||
|
|
||||||
(defclass FooBar [object]
|
(defclass FooBar [object]
|
||||||
"Yet Another Example Class"
|
"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
|
(defn --init-- [self x]
|
||||||
(fn [self]
|
(setv self.x x)
|
||||||
"Return our copy of x"
|
None)
|
||||||
self.x)]])
|
|
||||||
|
(defn get-x [self]
|
||||||
|
"Return our copy of x"
|
||||||
|
self.x))
|
||||||
|
|
||||||
|
|
||||||
You can also do class-level attributes. In Python::
|
You can also do class-level attributes. In Python::
|
||||||
@ -502,9 +499,9 @@ In Hy:
|
|||||||
.. code-block:: clj
|
.. code-block:: clj
|
||||||
|
|
||||||
(defclass Customer [models.Model]
|
(defclass Customer [models.Model]
|
||||||
[[name (models.CharField :max-length 255})]
|
[name (models.CharField :max-length 255})
|
||||||
[address (models.TextField)]
|
address (models.TextField)
|
||||||
[notes (models.TextField)]])
|
notes (models.TextField)])
|
||||||
|
|
||||||
Hy <-> Python interop
|
Hy <-> Python interop
|
||||||
=====================
|
=====================
|
||||||
|
@ -2238,15 +2238,15 @@ class HyASTCompiler(object):
|
|||||||
|
|
||||||
@builds("defclass")
|
@builds("defclass")
|
||||||
@checkargs(min=1)
|
@checkargs(min=1)
|
||||||
def compile_class_expression(self, expression):
|
def compile_class_expression(self, expressions):
|
||||||
expression.pop(0) # class
|
expressions.pop(0) # class
|
||||||
|
|
||||||
class_name = expression.pop(0)
|
class_name = expressions.pop(0)
|
||||||
|
|
||||||
if expression:
|
if expressions:
|
||||||
base_list = expression.pop(0)
|
base_list = expressions.pop(0)
|
||||||
if not isinstance(base_list, HyList):
|
if not isinstance(base_list, HyList):
|
||||||
raise HyTypeError(expression,
|
raise HyTypeError(expressions,
|
||||||
"Bases class must be a list")
|
"Bases class must be a list")
|
||||||
bases_expr, bases, _ = self._compile_collect(base_list)
|
bases_expr, bases, _ = self._compile_collect(base_list)
|
||||||
else:
|
else:
|
||||||
@ -2256,8 +2256,8 @@ class HyASTCompiler(object):
|
|||||||
body = Result()
|
body = Result()
|
||||||
|
|
||||||
# grab the doc string, if there is one
|
# grab the doc string, if there is one
|
||||||
if expression and isinstance(expression[0], HyString):
|
if expressions and isinstance(expressions[0], HyString):
|
||||||
docstring = expression.pop(0)
|
docstring = expressions.pop(0)
|
||||||
symb = HySymbol("__doc__")
|
symb = HySymbol("__doc__")
|
||||||
symb.start_line = docstring.start_line
|
symb.start_line = docstring.start_line
|
||||||
symb.start_column = docstring.start_column
|
symb.start_column = docstring.start_column
|
||||||
@ -2266,31 +2266,25 @@ class HyASTCompiler(object):
|
|||||||
docstring.start_column)
|
docstring.start_column)
|
||||||
body += body.expr_as_stmt()
|
body += body.expr_as_stmt()
|
||||||
|
|
||||||
if expression:
|
if expressions and isinstance(expressions[0], HyList) \
|
||||||
try:
|
and not isinstance(expressions[0], HyExpression):
|
||||||
body_expression = iter(expression.pop(0))
|
expr = expressions.pop(0)
|
||||||
except TypeError:
|
body += self.compile(
|
||||||
raise HyTypeError(
|
HyExpression([
|
||||||
expression,
|
HySymbol("setv")
|
||||||
"Wrong argument type for defclass attributes definition.")
|
] + expr).replace(expr)
|
||||||
for b in body_expression:
|
)
|
||||||
if isinstance(b, HyExpression):
|
|
||||||
b = macroexpand(b, self.module_name)
|
for expression in expressions:
|
||||||
if len(b) != 2:
|
body += self.compile(macroexpand(expression, self.module_name))
|
||||||
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 not body.stmts:
|
if not body.stmts:
|
||||||
body += ast.Pass(lineno=expression.start_line,
|
body += ast.Pass(lineno=expressions.start_line,
|
||||||
col_offset=expression.start_column)
|
col_offset=expressions.start_column)
|
||||||
|
|
||||||
return bases + ast.ClassDef(
|
return bases + ast.ClassDef(
|
||||||
lineno=expression.start_line,
|
lineno=expressions.start_line,
|
||||||
col_offset=expression.start_column,
|
col_offset=expressions.start_column,
|
||||||
decorator_list=[],
|
decorator_list=[],
|
||||||
name=ast_str(class_name),
|
name=ast_str(class_name),
|
||||||
keywords=[],
|
keywords=[],
|
||||||
|
@ -2,12 +2,11 @@
|
|||||||
|
|
||||||
(defclass FakeMeth []
|
(defclass FakeMeth []
|
||||||
"Mocking decorator class"
|
"Mocking decorator class"
|
||||||
[[rules {}]
|
[rules {}]
|
||||||
[route (fn [self rule &kwargs options]
|
(defn route [self rule &kwargs options]
|
||||||
(fn [f]
|
(fn [f]
|
||||||
(assoc self.rules rule (, f options))
|
(assoc self.rules rule (, f options))
|
||||||
f))]])
|
f)))
|
||||||
|
|
||||||
|
|
||||||
(defn test_route []
|
(defn test_route []
|
||||||
(let [[app (FakeMeth)]]
|
(let [[app (FakeMeth)]]
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
(defn test-defclass-attrs []
|
(defn test-defclass-attrs []
|
||||||
"NATIVE: test defclass attributes"
|
"NATIVE: test defclass attributes"
|
||||||
(defclass A []
|
(defclass A []
|
||||||
[[x 42]])
|
[x 42])
|
||||||
(assert (= A.x 42))
|
(assert (= A.x 42))
|
||||||
(assert (= (getattr (A) "x") 42)))
|
(assert (= (getattr (A) "x") 42)))
|
||||||
|
|
||||||
@ -31,9 +31,9 @@
|
|||||||
(defn test-defclass-attrs-fn []
|
(defn test-defclass-attrs-fn []
|
||||||
"NATIVE: test defclass attributes with fn"
|
"NATIVE: test defclass attributes with fn"
|
||||||
(defclass B []
|
(defclass B []
|
||||||
[[x 42]
|
[x 42
|
||||||
[y (fn [self value]
|
y (fn [self value]
|
||||||
(+ self.x value))]])
|
(+ self.x value))])
|
||||||
(assert (= B.x 42))
|
(assert (= B.x 42))
|
||||||
(assert (= (.y (B) 5) 47))
|
(assert (= (.y (B) 5) 47))
|
||||||
(let [[b (B)]]
|
(let [[b (B)]]
|
||||||
@ -44,17 +44,17 @@
|
|||||||
(defn test-defclass-dynamic-inheritance []
|
(defn test-defclass-dynamic-inheritance []
|
||||||
"NATIVE: test defclass with dynamic inheritance"
|
"NATIVE: test defclass with dynamic inheritance"
|
||||||
(defclass A [((fn [] (if true list dict)))]
|
(defclass A [((fn [] (if true list dict)))]
|
||||||
[[x 42]])
|
[x 42])
|
||||||
(assert (isinstance (A) list))
|
(assert (isinstance (A) list))
|
||||||
(defclass A [((fn [] (if false list dict)))]
|
(defclass A [((fn [] (if false list dict)))]
|
||||||
[[x 42]])
|
[x 42])
|
||||||
(assert (isinstance (A) dict)))
|
(assert (isinstance (A) dict)))
|
||||||
|
|
||||||
|
|
||||||
(defn test-defclass-no-fn-leak []
|
(defn test-defclass-no-fn-leak []
|
||||||
"NATIVE: test defclass attributes with fn"
|
"NATIVE: test defclass attributes with fn"
|
||||||
(defclass A []
|
(defclass A []
|
||||||
[[x (fn [] 1)]])
|
[x (fn [] 1)])
|
||||||
(try
|
(try
|
||||||
(do
|
(do
|
||||||
(x)
|
(x)
|
||||||
@ -64,13 +64,13 @@
|
|||||||
(defn test-defclass-docstring []
|
(defn test-defclass-docstring []
|
||||||
"NATIVE: test defclass docstring"
|
"NATIVE: test defclass docstring"
|
||||||
(defclass A []
|
(defclass A []
|
||||||
[[--doc-- "doc string"]
|
[--doc-- "doc string"
|
||||||
[x 1]])
|
x 1])
|
||||||
(setv a (A))
|
(setv a (A))
|
||||||
(assert (= a.__doc__ "doc string"))
|
(assert (= a.__doc__ "doc string"))
|
||||||
(defclass B []
|
(defclass B []
|
||||||
"doc string"
|
"doc string"
|
||||||
[[x 1]])
|
[x 1])
|
||||||
(setv b (B))
|
(setv b (B))
|
||||||
(assert (= b.x 1))
|
(assert (= b.x 1))
|
||||||
(assert (= b.__doc__ "doc string"))
|
(assert (= b.__doc__ "doc string"))
|
||||||
@ -78,7 +78,7 @@
|
|||||||
"begin a very long multi-line string to make
|
"begin a very long multi-line string to make
|
||||||
sure that it comes out the way we hope
|
sure that it comes out the way we hope
|
||||||
and can span 3 lines end."
|
and can span 3 lines end."
|
||||||
[[x 1]])
|
[x 1])
|
||||||
(setv mL (MultiLine))
|
(setv mL (MultiLine))
|
||||||
(assert (= mL.x 1))
|
(assert (= mL.x 1))
|
||||||
(assert (in "begin" mL.__doc__))
|
(assert (in "begin" mL.__doc__))
|
||||||
@ -86,8 +86,26 @@
|
|||||||
|
|
||||||
(defn test-defclass-macroexpand []
|
(defn test-defclass-macroexpand []
|
||||||
"NATIVE: test defclass with macro expand"
|
"NATIVE: test defclass with macro expand"
|
||||||
(defmacro M [] `[x (fn [self x] (setv self._x x))])
|
(defmacro M [] `(defn x [self x] (setv self._x x)))
|
||||||
(defclass A [] [(M)])
|
(defclass A [] (M))
|
||||||
(setv a (A))
|
(setv a (A))
|
||||||
(a.x 1)
|
(a.x 1)
|
||||||
(assert (= 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]
|
(defclass HyTestMatrix [list]
|
||||||
[[--matmul--
|
[--matmul--
|
||||||
(fn [self other]
|
(fn [self other]
|
||||||
(let [[n (len self)]
|
(let [[n (len self)]
|
||||||
[m (len (. other [0]))]
|
[m (len (. other [0]))]
|
||||||
[result []]]
|
[result []]]
|
||||||
(for [i (range m)]
|
(for [i (range m)]
|
||||||
(let [[result-row []]]
|
(let [[result-row []]]
|
||||||
(for [j (range n)]
|
(for [j (range n)]
|
||||||
(let [[dot-product 0]]
|
(let [[dot-product 0]]
|
||||||
(for [k (range (len (. self [0])))]
|
(for [k (range (len (. self [0])))]
|
||||||
(+= dot-product (* (. self [i] [k])
|
(+= dot-product (* (. self [i] [k])
|
||||||
(. other [k] [j]))))
|
(. other [k] [j]))))
|
||||||
(.append result-row dot-product)))
|
(.append result-row dot-product)))
|
||||||
(.append result result-row)))
|
(.append result result-row)))
|
||||||
result))]])
|
result))])
|
||||||
|
|
||||||
(def first-test-matrix (HyTestMatrix [[1 2 3]
|
(def first-test-matrix (HyTestMatrix [[1 2 3]
|
||||||
[4 5 6]
|
[4 5 6]
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
(with-decorator bardec
|
(with-decorator bardec
|
||||||
(defclass cls []
|
(defclass cls []
|
||||||
[[my_attr 456]]))
|
[my_attr 456]))
|
||||||
|
|
||||||
(defn test-decorator-clobbing []
|
(defn test-decorator-clobbing []
|
||||||
"NATIVE: Tests whether nested decorators work"
|
"NATIVE: Tests whether nested decorators work"
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
(defclass WithTest [object]
|
(defclass WithTest [object]
|
||||||
[(--init--
|
(defn --init-- [self val]
|
||||||
(fn [self val]
|
(setv self.val val)
|
||||||
(setv self.val val)
|
None)
|
||||||
None))
|
|
||||||
|
|
||||||
(--enter--
|
(defn --enter-- [self]
|
||||||
(fn [self]
|
self.val)
|
||||||
self.val))
|
|
||||||
|
|
||||||
(--exit--
|
(defn --exit-- [self type value traceback]
|
||||||
(fn [self type value traceback]
|
(setv self.val None)))
|
||||||
(setv self.val None)))])
|
|
||||||
|
|
||||||
(defn test-single-with []
|
(defn test-single-with []
|
||||||
"NATIVE: test a single with"
|
"NATIVE: test a single with"
|
||||||
|
Loading…
Reference in New Issue
Block a user