Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/basilisp/core.lpy
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,17 @@
v
(.-name v)))

(defn ^:inline tagged-literal
"Construct a data representation of a tagged literal from a
tag symbol and a form."
[tag form]
(basilisp.lang.tagged/tagged-literal tag form))

(defn ^:inline tagged-literal?
"Return true if the value is the data representation of a tagged literal"
[o]
(instance? basilisp.lang.tagged/TaggedLiteral o))

(defn ^:inline namespace
"Return the namespace of a symbol or keyword, or ``nil`` if no namespace."
[v]
Expand Down
2 changes: 2 additions & 0 deletions src/basilisp/lang/compiler/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,7 @@ def _var_ns_as_python_sym(name: str) -> str:
_SEQ_ALIAS = genname("seq")
_SET_ALIAS = genname("lset")
_SYM_ALIAS = genname("sym")
_TAGGED_ALIAS = genname("tagged")
_VEC_ALIAS = genname("vec")
_VOLATILE_ALIAS = genname("volatile")
_VAR_ALIAS = genname("Var")
Expand Down Expand Up @@ -731,6 +732,7 @@ def _var_ns_as_python_sym(name: str) -> str:
"basilisp.lang.seq": _SEQ_ALIAS,
"basilisp.lang.set": _SET_ALIAS,
"basilisp.lang.symbol": _SYM_ALIAS,
"basilisp.lang.tagged": _TAGGED_ALIAS,
"basilisp.lang.vector": _VEC_ALIAS,
"basilisp.lang.volatile": _VOLATILE_ALIAS,
"basilisp.lang.util": _UTIL_ALIAS,
Expand Down
1 change: 1 addition & 0 deletions src/basilisp/lang/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@ class Namespace(ReferenceBase):
"basilisp.lang.seq",
"basilisp.lang.set",
"basilisp.lang.symbol",
"basilisp.lang.tagged",
"basilisp.lang.vector",
"basilisp.lang.volatile",
"basilisp.lang.util",
Expand Down
85 changes: 85 additions & 0 deletions src/basilisp/lang/tagged.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from typing import (
Optional,
TypeVar,
Union,
)

from typing_extensions import Unpack

from basilisp.lang.interfaces import (
ILispObject,
ILookup,
)

from basilisp.lang.keyword import keyword
from basilisp.lang.obj import PrintSettings, lrepr
from basilisp.lang.symbol import Symbol

K = TypeVar("K")
V = TypeVar("V")
T = Union[None, V, Symbol]

_TAG_KW = keyword("tag")
_FORM_KW = keyword("form")

class TaggedLiteral(
ILispObject,
ILookup[K, T],
):
"""Basilisp TaggedLiteral. https://clojure.org/reference/reader#tagged_literals
"""

__slots__ = ("_tag", "_form", "_hash")

def __init__(
self, tag: Symbol, form
) -> None:
self._tag = tag
self._form = form
self._hash : Optional[int] = None

@property
def tag(self) -> Symbol:
return self._tag

@property
def form(self):
return self._form

def __bool__(self):
return True

def __eq__(self, other):
if self is other:
return True
if not isinstance(other, TaggedLiteral):
return NotImplemented
return self._tag == other._tag and self._form == other._form

def __hash__(self):
if self._hash is None:
self._hash = hash((self._tag, self._form))
return self._hash

def __getitem__(self, item):
return self.val_at(item)

def val_at(self, k: K, default: Optional[V] = None) -> T:
if k == _TAG_KW:
return self._tag
elif k == _FORM_KW:
return self._form
else:
return default

def _lrepr(self, **kwargs: Unpack[PrintSettings]) -> str:
return f"#{self._tag} {lrepr(self._form, **kwargs)}"

def tagged_literal(
tag: Symbol, form
):
"""Construct a data representation of a tagged literal from a
tag symbol and a form."""
if not isinstance(tag, Symbol):
raise TypeError(f"tag must be a Symbol, not '{type(tag)}'")
return TaggedLiteral(tag, form)
16 changes: 16 additions & 0 deletions tests/basilisp/tagged_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from basilisp.lang.symbol import symbol
from basilisp.lang.tagged import tagged_literal

def test_tagged_literal():
tag = symbol("tag")
form = 1
tagged = tagged_literal(tag, form)
assert tagged.tag == tag
assert tagged.form == form

def test_tagged_literal_str_and_repr():
tag = symbol("tag")
form = 1
tagged = tagged_literal(tag, form)
assert str(tagged) == "#tag 1"
assert repr(tagged) == "#tag 1"
33 changes: 33 additions & 0 deletions tests/basilisp/test_tagged.lpy
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
(ns tests.basilisp.test-tagged
(:require
[basilisp.test :refer [deftest is testing]]))

(deftest tagged-literal-test
(let [tag 'tag
form 1
tagged (tagged-literal tag form)]
(testing "equality"
(is (= tagged tagged))
(is (= tagged (tagged-literal tag form)))
(is (not= tagged (tagged-literal 'foo form)))
(is (not= tagged (tagged-literal tag 2))))

(testing "accessors"
(is (= tag (:tag tagged)))
(is (= form (:form tagged)))
(is (nil? (:key tagged)))
(is (= ::default (:key tagged ::default))))

(testing "predicate"
(is (true? (tagged-literal? tagged)))
(is (false? (tagged-literal? nil)))
(is (false? (tagged-literal? 0)))
(is (false? (tagged-literal? ::foo))))

(testing "printing"
(is (= "#tag 1" (pr-str tagged)))
(is (= "#js []" (pr-str (tagged-literal 'js []))))
(is (= "#js {}" (pr-str (tagged-literal 'js {})))))

(testing "validation"
(is (thrown? TypeError (tagged-literal 1 1))))))
Loading