Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
f2da6f2
Protocol initial impl
chrisrink10 Dec 31, 2019
0dbd507
Merge branch 'master' into feature/protocols-and-volatiles
chrisrink10 Dec 31, 2019
940678d
Merge branch 'master' into feature/protocols-and-volatiles
chrisrink10 Jan 26, 2020
f67a2bb
Merge branch 'master' into feature/protocols-and-volatiles
chrisrink10 Jan 28, 2020
a55cf9e
Merge branch 'master' into feature/protocols-and-volatiles
chrisrink10 Jan 28, 2020
e80de30
Changelog
chrisrink10 Jan 28, 2020
e0c73a0
Merge branch 'master' into feature/protocols-and-volatiles
chrisrink10 Feb 26, 2020
b308c54
Merge branch 'master' into feature/protocols-and-volatiles
chrisrink10 Mar 12, 2020
4246211
Merge branch 'master' into feature/protocols-and-volatiles
chrisrink10 Mar 14, 2020
9817ab5
Merge branch 'master' into feature/protocols-and-volatiles
chrisrink10 Mar 14, 2020
8e230c8
Merge branch 'master' into feature/protocols-and-volatiles
chrisrink10 Mar 15, 2020
8af1a0e
Merge branch 'master' into feature/protocols-and-volatiles
chrisrink10 Mar 17, 2020
35cdd39
Volatile tests
chrisrink10 Mar 18, 2020
d8df16f
Some small changes
chrisrink10 Mar 18, 2020
7709792
Getting there
chrisrink10 Mar 18, 2020
9762f01
Docstrings
chrisrink10 Mar 18, 2020
788e121
Simple tests
chrisrink10 Mar 19, 2020
36d8e47
Tests
chrisrink10 Mar 19, 2020
4f6bb29
Extend tests
chrisrink10 Mar 19, 2020
31ccec8
Merge branch 'master' into feature/protocols-and-volatiles
chrisrink10 Mar 20, 2020
b5d0cfa
Lots of stuff all mashed up
chrisrink10 Mar 22, 2020
ecf23c4
Merge branch 'master' into feature/protocols-and-volatiles
chrisrink10 Mar 22, 2020
295258d
Compiler support
chrisrink10 Mar 22, 2020
c14749b
Oopsie
chrisrink10 Mar 22, 2020
1e4ddde
Merge branch 'master' into feature/protocols-and-volatiles
chrisrink10 Apr 1, 2020
6ab555c
Merge branch 'master' into feature/protocols-and-volatiles
chrisrink10 May 16, 2020
028a30a
Merge branch 'master' into feature/protocols-and-volatiles
chrisrink10 May 26, 2020
3599b16
Merge branch 'master' into feature/protocols-and-volatiles
chrisrink10 May 29, 2020
4a39175
Fix gen-interface call
chrisrink10 May 29, 2020
f1e7b73
Fix that redef
chrisrink10 May 29, 2020
a870e84
Fix a bunch of buggos
chrisrink10 May 29, 2020
852fd27
Do better
chrisrink10 May 29, 2020
dbbfce8
Fix definterface and defprotocol this-ns
chrisrink10 May 29, 2020
f75d4ea
Verify that target-types implement all methods
chrisrink10 May 29, 2020
2d60f47
Use singledispatch
chrisrink10 May 30, 2020
9d6a82d
Convert basilisp.walk to use a protocol
chrisrink10 May 30, 2020
d971a9c
Switch MRO
chrisrink10 May 30, 2020
83022db
Slightly more tests
chrisrink10 May 30, 2020
8a6309b
Support multi-arity fns
chrisrink10 May 30, 2020
eef0d34
Support multiple-arity protocols
chrisrink10 May 31, 2020
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Added metadata about the function or method context of a Lisp AST node in the `NodeEnv` (#548)
* Added `reify*` special form (#425)
* Added support for multi-arity methods on `definterface` (#538)
* Added support for Protocols (#460)
* Added support for Volatiles (#460)

### Fixed
* Fixed a bug where the Basilisp AST nodes for return values of `deftype` members could be marked as _statements_ rather than _expressions_, resulting in an incorrect `nil` return (#523)
Expand Down
410 changes: 400 additions & 10 deletions src/basilisp/core.lpy

Large diffs are not rendered by default.

15 changes: 14 additions & 1 deletion src/basilisp/lang/compiler/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
SYM_PRIVATE_META_KEY,
SYM_PROPERTY_META_KEY,
SYM_STATICMETHOD_META_KEY,
VAR_IS_PROTOCOL_META_KEY,
SpecialForm,
)
from basilisp.lang.compiler.exception import CompilerException, CompilerPhase
Expand Down Expand Up @@ -150,6 +151,7 @@
# Constants used in analyzing
AS = kw.keyword("as")
IMPLEMENTS = kw.keyword("implements")
INTERFACE = kw.keyword("interface")
STAR_STAR = sym.symbol("**")
_DOUBLE_DOT_MACRO_NAME = ".."
_BUILTINS_NS = "python"
Expand Down Expand Up @@ -1507,6 +1509,9 @@ def __deftype_or_reify_impls( # pylint: disable=too-many-branches,too-many-loca
return interfaces, members


_var_is_protocol = _meta_getter(VAR_IS_PROTOCOL_META_KEY)


def __deftype_and_reify_impls_are_all_abstract( # pylint: disable=too-many-branches,too-many-locals
special_form: sym.Symbol,
fields: Iterable[str],
Expand Down Expand Up @@ -1543,7 +1548,15 @@ def __deftype_and_reify_impls_are_all_abstract( # pylint: disable=too-many-bran
"and cannot be checked for abstractness; deferring to runtime",
)
return False
interface_type = interface.var.value

# Protocols are defined as maps, with the interface being simply a member
# of the map, denoted by the keyword `:interface`.
if _var_is_protocol(interface.var):
proto_map = interface.var.value
assert isinstance(proto_map, lmap.Map)
interface_type = proto_map.val_at(INTERFACE)
else:
interface_type = interface.var.value

if interface_type is object:
continue
Expand Down
2 changes: 2 additions & 0 deletions src/basilisp/lang/compiler/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,5 @@ class SpecialForm:
LINE_KW = kw.keyword("line")
NAME_KW = kw.keyword("name")
NS_KW = kw.keyword("ns")

VAR_IS_PROTOCOL_META_KEY = kw.keyword("protocol", "basilisp.core")
111 changes: 94 additions & 17 deletions src/basilisp/lang/compiler/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
SYM_DYNAMIC_META_KEY,
SYM_NO_WARN_ON_REDEF_META_KEY,
SYM_REDEF_META_KEY,
VAR_IS_PROTOCOL_META_KEY,
)
from basilisp.lang.compiler.exception import CompilerException, CompilerPhase
from basilisp.lang.compiler.nodes import (
Expand Down Expand Up @@ -1266,6 +1267,46 @@ def __deftype_member_to_py_ast(
return handle_deftype_member(ctx, node)


def __deftype_or_reify_bases_to_py_ast(
ctx: GeneratorContext, node: Union[DefType, Reify]
) -> List[ast.AST]:
"""Return a list of AST nodes for the base classes for a `deftype*` or `reify*`."""
assert node.op in {NodeOp.DEFTYPE, NodeOp.REIFY}

bases: List[ast.AST] = []
for base in node.interfaces:
base_node = gen_py_ast(ctx, base)
assert (
count(base_node.dependencies) == 0
), "Class and host form nodes do not have dependencies"

# Protocols are defined as Maps
if (
isinstance(base, VarRef)
and base.var.meta is not None
and base.var.meta.val_at(VAR_IS_PROTOCOL_META_KEY)
):
bases.append(
ast.Call(
func=ast.Attribute(
value=base_node.node, attr="val_at", ctx=ast.Load()
),
args=[
ast.Call(
func=_NEW_KW_FN_NAME,
args=[ast.Constant("interface")],
keywords=[],
)
],
keywords=[],
)
)
else:
bases.append(base_node.node)

return bases


@_with_ast_loc
def _deftype_to_py_ast( # pylint: disable=too-many-branches,too-many-locals
ctx: GeneratorContext, node: DefType
Expand All @@ -1275,13 +1316,7 @@ def _deftype_to_py_ast( # pylint: disable=too-many-branches,too-many-locals
type_name = munge(node.name)
ctx.symbol_table.new_symbol(sym.symbol(node.name), type_name, LocalType.DEFTYPE)

bases = []
for base in node.interfaces:
base_node = gen_py_ast(ctx, base)
assert (
count(base_node.dependencies) == 0
), "Class and host form nodes do not have dependencies"
bases.append(base_node.node)
bases = __deftype_or_reify_bases_to_py_ast(ctx, node)

with ctx.new_symbol_table(node.name):
fields = []
Expand Down Expand Up @@ -1469,6 +1504,35 @@ def __fn_args_to_py_ast(
return fn_args, varg, fn_body_ast


def __fn_decorator(arities: Iterable[int], has_rest_arg: bool = False,) -> ast.Call:
return ast.Call(
func=_BASILISP_FN_FN_NAME,
args=[],
keywords=[
ast.keyword(
arg="arities",
value=ast.Tuple(
elts=list(
chain(
map(ast.Constant, arities),
[
ast.Call(
func=_NEW_KW_FN_NAME,
args=[ast.Constant("rest")],
keywords=[],
)
]
if has_rest_arg
else [],
)
),
ctx=ast.Load(),
),
)
],
)


def __fn_meta(
ctx: GeneratorContext, meta_node: Optional[MetaNode] = None
) -> Tuple[Iterable[ast.AST], Iterable[ast.AST]]:
Expand Down Expand Up @@ -1549,7 +1613,14 @@ def __single_arity_fn_to_py_ast(
chain(
__kwargs_support_decorator(node),
meta_decorators,
[_BASILISP_FN_FN_NAME],
[
__fn_decorator(
(len(fn_args),)
if not method.is_variadic
else (),
has_rest_arg=method.is_variadic,
)
],
[_TRAMPOLINE_FN_NAME]
if ctx.recur_point.has_recur
else [],
Expand Down Expand Up @@ -1714,7 +1785,17 @@ def fn(*args):
kw_defaults=[],
),
body=body,
decorator_list=list(chain(meta_decorators, [_BASILISP_FN_FN_NAME])),
decorator_list=list(
chain(
meta_decorators,
[
__fn_decorator(
arity_map.keys(),
has_rest_arg=default_name is not None,
)
],
)
),
returns=None,
)
],
Expand Down Expand Up @@ -2291,14 +2372,10 @@ def _reify_to_py_ast(
else:
meta_ast = None

bases: List[ast.AST] = [_BASILISP_WITH_META_INTERFACE_NAME]
for base in node.interfaces:
base_node = gen_py_ast(ctx, base)
assert (
count(base_node.dependencies) == 0
), "Class and host form nodes do not have dependencies"
bases.append(base_node.node)

bases: List[ast.AST] = [
_BASILISP_WITH_META_INTERFACE_NAME,
*__deftype_or_reify_bases_to_py_ast(ctx, node),
]
type_name = munge(genname("ReifiedType"))

with ctx.new_symbol_table("reify"):
Expand Down
2 changes: 1 addition & 1 deletion src/basilisp/lang/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
T = TypeVar("T")


class List(IWithMeta, ISeq[T], IPersistentList[T]):
class List(IPersistentList[T], ISeq[T], IWithMeta):
"""Basilisp List. Delegates internally to a pyrsistent.PList object.

Do not instantiate directly. Instead use the l() and list() factory
Expand Down
2 changes: 1 addition & 1 deletion src/basilisp/lang/map.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
_ENTRY_SENTINEL = object()


class Map(ILispObject, IWithMeta, IPersistentMap[K, V]):
class Map(IPersistentMap[K, V], ILispObject, IWithMeta):
"""Basilisp Map. Delegates internally to a pyrsistent.PMap object.
Do not instantiate directly. Instead use the m() and map() factory
methods below."""
Expand Down
20 changes: 13 additions & 7 deletions src/basilisp/lang/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ class Namespace(ReferenceBase):
[
"attr",
"builtins",
"functools",
"io",
"importlib",
"operator",
Expand Down Expand Up @@ -1149,7 +1150,7 @@ def contains(coll, k):
def get(m, k, default=None):
"""Return the value of k in m. Return default if k not found in m."""
if isinstance(m, ILookup):
return m.val_at(k, default=default)
return m.val_at(k, default)

try:
return m[k]
Expand Down Expand Up @@ -1453,14 +1454,19 @@ def wrapped_f(*args, **kwargs):
return wrapped_f


def _basilisp_fn(f):
def _basilisp_fn(arities: Tuple[Union[int, kw.Keyword]]):
"""Create a Basilisp function, setting meta and supplying a with_meta
method implementation."""
assert not hasattr(f, "meta")
f._basilisp_fn = True
f.meta = None
f.with_meta = partial(_fn_with_meta, f)
return f

def wrap_fn(f):
assert not hasattr(f, "meta")
f._basilisp_fn = True
f.arities = lset.set(arities)
f.meta = None
f.with_meta = partial(_fn_with_meta, f)
return f

return wrap_fn


def _basilisp_type(
Expand Down
2 changes: 1 addition & 1 deletion src/basilisp/lang/set.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
T = TypeVar("T")


class Set(IWithMeta, ILispObject, IPersistentSet[T]):
class Set(IPersistentSet[T], ILispObject, IWithMeta):
"""Basilisp Set. Delegates internally to a pyrsistent.PSet object.

Do not instantiate directly. Instead use the s() and set() factory
Expand Down
2 changes: 1 addition & 1 deletion src/basilisp/lang/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
T = TypeVar("T")


class Vector(ILispObject, IWithMeta, IPersistentVector[T]):
class Vector(IPersistentVector[T], ILispObject, IWithMeta):
"""Basilisp Vector. Delegates internally to a pyrsistent.PVector object.
Do not instantiate directly. Instead use the v() and vec() factory
methods below."""
Expand Down
69 changes: 42 additions & 27 deletions src/basilisp/walk.lpy
Original file line number Diff line number Diff line change
@@ -1,5 +1,46 @@
(ns basilisp.walk)

(defprotocol IWalkable
(walk* [this inner outer]
"Walk the data structure applying `inner` to each element of the data structure,
if any, and then applying `outer` to the result."))

(extend-protocol IWalkable
basilisp.lang.interfaces/IPersistentList
(walk* [this inner outer]
(outer (apply list (map inner this))))

basilisp.lang.interfaces/IMapEntry
(walk* [this inner outer]
(outer (map-entry (inner (key this)) (inner (val this)))))

basilisp.lang.interfaces/ISeq
(walk* [this inner outer]
(outer (doall (map inner this))))

basilisp.lang.interfaces/IPersistentVector
(walk* [this inner outer]
(outer (apply vector (map inner this))))

basilisp.lang.interfaces/IPersistentMap
(walk* [this inner outer]
(outer (apply hash-map (mapcat inner this))))

basilisp.lang.interfaces/IPersistentSet
(walk* [this inner outer]
(outer (apply hash-set (map inner this))))

basilisp.lang.interfaces/IRecord
(walk* [this inner outer]
(outer (reduce (fn [rec field]
(conj rec (inner field)))
this
this)))

python/object
(walk* [this ^:no-warn-when-unused inner outer]
(outer this)))

(defn walk
"Walk an arbitrary, possibly nested data structure, applying inner to each
element of form and then applying outer to the resulting form.
Expand All @@ -8,33 +49,7 @@

Lazy sequences will be completely consumed (and thus may not be infinite)."
[inner outer form]
(cond
(list? form)
(outer (apply list (map inner form)))

(map-entry? form)
(outer (map-entry (inner (key form)) (inner (val form))))

(seq? form)
(outer (doall (map inner form)))

(vector? form)
(outer (apply vector (map inner form)))

(map? form)
(outer (apply hash-map (mapcat inner form)))

(set? form)
(outer (apply hash-set (map inner form)))

(record? form)
(outer (reduce (fn [rec field]
(conj rec (inner field)))
form
form))

:else
(outer form)))
(walk* form inner outer))

(defn postwalk
"Walk form using depth-first, post-order traversal, applying f to each form
Expand Down
Loading