From fc3a33f825ee2f6e81430d0f17507a94308ea6b3 Mon Sep 17 00:00:00 2001 From: Christopher Rink Date: Sun, 28 Jul 2019 22:10:46 -0400 Subject: [PATCH] Fix some things --- src/basilisp/core.lpy | 18 ++++++++++++++++-- src/basilisp/lang/compiler/analyzer.py | 7 ++++--- src/basilisp/lang/reader.py | 1 + tests/basilisp/core_macros_test.lpy | 6 ++++++ 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/basilisp/core.lpy b/src/basilisp/core.lpy index de8e4392..c490b655 100644 --- a/src/basilisp/core.lpy +++ b/src/basilisp/core.lpy @@ -1947,9 +1947,23 @@ (defmacro .. "Expand into nested method calls on the returned objects from previous - method calls." + method calls. + + Successive method invocations are represented as successive lists: + + (.. \"abc\" (lower)) ;=> (. \"abc\" lower) + (.. \"abc\" lower (split \",\")) ;=> (. (. \"abc\" lower) split \",\") + + Methods invoked without arguments may be supplied as bare symbols." [x & method-calls] - ) + (if (seq method-calls) + (let [joining (first method-calls)] + `(.. + ~(if (seq? joining) + (apply list '. x joining) + (list '. x joining)) + ~@(rest method-calls))) + x)) (defmacro new "Create a new instance of class with args. diff --git a/src/basilisp/lang/compiler/analyzer.py b/src/basilisp/lang/compiler/analyzer.py index 05bdcb22..89aee106 100644 --- a/src/basilisp/lang/compiler/analyzer.py +++ b/src/basilisp/lang/compiler/analyzer.py @@ -138,6 +138,7 @@ # Constants used in analyzing AS = kw.keyword("as") IMPLEMENTS = kw.keyword("implements") +ns_name_chars = re.compile(r"\w|-|\+|\*|\?|/|\=|\\|!|&|%|>|<|\$") _BUILTINS_NS = "python" # Symbols to be ignored for unused symbol warnings @@ -2048,7 +2049,7 @@ def _list_node(ctx: AnalyzerContext, form: ISeq) -> Node: return handle_special_form(ctx, form) elif s.name.startswith(".-"): return _host_prop_ast(ctx, form) - elif s.name.startswith("."): + elif s.name.startswith(".") and reader.name_chars.match(s.name[1:]): return _host_call_ast(ctx, form) return _invoke_ast(ctx, form) @@ -2094,7 +2095,7 @@ def __resolve_namespaced_symbol( # pylint: disable=too-many-branches form=form, class_=class_, target=target, env=ctx.get_node_env() ) - if "." in form.name: + if "." in form.name and form.name != "..": raise AnalyzerException( "symbol names may not contain the '.' operator", form=form ) @@ -2206,7 +2207,7 @@ def _resolve_sym( # (Classname. *args) # (aliased.Classname. *args) # (fully.qualified.Classname. *args) - if form.ns is None and form.name.endswith("."): + if form.ns is None and form.name.endswith(".") and form.name != "..": try: ns, name = form.name[:-1].rsplit(".", maxsplit=1) form = sym.symbol(name, ns=ns) diff --git a/src/basilisp/lang/reader.py b/src/basilisp/lang/reader.py index 26bcac61..8c5f5b3e 100644 --- a/src/basilisp/lang/reader.py +++ b/src/basilisp/lang/reader.py @@ -51,6 +51,7 @@ from basilisp.util import Maybe, partition ns_name_chars = re.compile(r"\w|-|\+|\*|\?|/|\=|\\|!|&|%|>|<|\$|\.") +name_chars = re.compile(r"\w|-|\+|\*|\?|/|\=|\\|!|&|%|>|<|\$") alphanumeric_chars = re.compile(r"\w") begin_num_chars = re.compile(r"[0-9\-]") num_chars = re.compile("[0-9]") diff --git a/tests/basilisp/core_macros_test.lpy b/tests/basilisp/core_macros_test.lpy index adc5508d..dbd0b455 100644 --- a/tests/basilisp/core_macros_test.lpy +++ b/tests/basilisp/core_macros_test.lpy @@ -232,6 +232,12 @@ (is (= nil (comment 1))) (is (= nil (comment [1 2 3])))) +(deftest double-dot-test + (is (= "abc" (.. "abc"))) + (is (= "ABC" (.. "abc" (upper)))) + (is (not (.. "abc" (upper) (islower)))) + (is (= #py ["A" "B" "C"] (.. "a,b,c" upper (split ","))))) + (deftest if-let-test (is (= :a (if-let [a :a] a :b))) (is (= 2 (if-let [a 1] (inc a) :b)))