Skip to content

Commit d503242

Browse files
authored
Support Clojure's object instantiation syntax (#152)
* Support Clojure's object instantiation syntax * Add an extra test case * Disable linter :shame:
1 parent 8c858d0 commit d503242

File tree

3 files changed

+54
-1
lines changed

3 files changed

+54
-1
lines changed

basilisp/compiler.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1459,12 +1459,23 @@ def _resolve_sym_var(ctx: CompilerContext, v: Var) -> Optional[str]:
14591459
return None
14601460

14611461

1462-
def _resolve_sym(ctx: CompilerContext, form: sym.Symbol) -> Optional[str]:
1462+
def _resolve_sym(ctx: CompilerContext, form: sym.Symbol) -> Optional[str]: # noqa: C901
14631463
"""Resolve a Basilisp symbol down to a Python Name (or Attribute).
14641464
14651465
If the symbol cannot be resolved or is specifically marked to prefer Var
14661466
indirection, then this function will return None. _sym_ast will generate a
14671467
Var.find call for runtime resolution."""
1468+
# Support special class-name syntax to instantiate new classes
1469+
# (Classname. *args)
1470+
# (aliased.Classname. *args)
1471+
# (fully.qualified.Classname. *args)
1472+
if form.ns is None and form.name.endswith('.'):
1473+
try:
1474+
ns, name = form.name[:-1].rsplit('.', maxsplit=1)
1475+
form = sym.symbol(name, ns=ns)
1476+
except ValueError:
1477+
form = sym.symbol(form.name[:-1])
1478+
14681479
# Attempt to resolve any symbol with a namespace to a direct Python call
14691480
if form.ns is not None:
14701481
if form.ns == _BUILTINS_NS:

basilisp/core/__init__.lpy

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,39 @@
684684
([m k default]
685685
(basilisp.lang.runtime/get m k default)))
686686

687+
(defmacro new
688+
"Create a new instance of class with args.
689+
690+
New objects may be created as any of:
691+
(new builtins/str *args)
692+
(new builtins.str *args)
693+
(new builtins.str. *args)
694+
695+
This is compatibility syntax for Clojure, since Python (and therefore
696+
Basilisp) do not require the new keyword for object instantiation."
697+
[class & args]
698+
(cond
699+
(not (symbol? class))
700+
(throw
701+
(ex-info "Expected a class name as a symbol"
702+
{:class-name class}))
703+
704+
(namespace class)
705+
(let [n (name class)
706+
ns (namespace class)
707+
s (symbol (str ns "."
708+
(if (.endswith n ".")
709+
n
710+
(str n "."))))]
711+
`(~s ~@args))
712+
713+
:else
714+
(let [n (name class)
715+
s (symbol (if (.endswith n ".")
716+
n
717+
(str n ".")))]
718+
`(~s ~@args))))
719+
687720
(defn range
688721
"Return a range of integers from start. If end is specified, the
689722
sequence will terminate at end."

tests/compiler_test.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,15 @@ def test_truthiness(ns_var: Var):
350350
assert kw.keyword("a") == lcompile("(if #{true} :a :b)")
351351

352352

353+
def test_interop_new(ns_var: Var):
354+
assert "hi" == lcompile('(builtins.str. "hi")')
355+
assert "1" == lcompile('(builtins.str. 1)')
356+
assert sym.symbol('hi') == lcompile('(basilisp.lang.symbol.Symbol. "hi")')
357+
358+
with pytest.raises(AttributeError):
359+
lcompile('(builtins.str "hi")')
360+
361+
353362
def test_interop_call(ns_var: Var):
354363
assert lcompile('(. "ALL-UPPER" lower)') == "all-upper"
355364
assert lcompile('(.upper "lower-string")') == "LOWER-STRING"

0 commit comments

Comments
 (0)