Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Fix a bug where seqs were not considered valid input for matching clauses of the `case` macro (#1148)
* Fix a bug where `py->lisp` did not keywordize string keys potentially containing namespaces (#1156)
* Fix a bug where anonymous functions using the `#(...)` reader syntax were not properly expanded in a syntax quote (#1160)
* Fix a bug where certain types of objects (such as objects created via `deftype`) could not be unquoted correctly in macros (#1153)

## [v0.3.3]
### Added
Expand Down
92 changes: 21 additions & 71 deletions src/basilisp/lang/compiler/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import functools
import hashlib
import logging
import pickle # nosec B403
import re
import uuid
from collections.abc import Collection, Iterable, Mapping, MutableMapping
Expand Down Expand Up @@ -95,7 +96,7 @@
ast_ClassDef,
ast_FunctionDef,
)
from basilisp.lang.interfaces import IMeta, IRecord, ISeq, ISeqable, IType
from basilisp.lang.interfaces import IMeta, ISeq
from basilisp.lang.runtime import CORE_NS
from basilisp.lang.runtime import NS_VAR_NAME as LISP_NS_VAR
from basilisp.lang.runtime import BasilispModule, Var
Expand Down Expand Up @@ -764,6 +765,7 @@ def _var_ns_as_python_sym(name: str) -> str:
_ATTR_CLASS_DECORATOR_NAME = _load_attr(f"{_ATTR_ALIAS}.define")
_ATTR_FROZEN_DECORATOR_NAME = _load_attr(f"{_ATTR_ALIAS}.frozen")
_ATTRIB_FIELD_FN_NAME = _load_attr(f"{_ATTR_ALIAS}.field")
_BASILISP_LOAD_CONSTANT_NAME = _load_attr(f"{_RUNTIME_ALIAS}._load_constant")
_COERCE_SEQ_FN_NAME = _load_attr(f"{_RUNTIME_ALIAS}.to_seq")
_BASILISP_FN_FN_NAME = _load_attr(f"{_RUNTIME_ALIAS}._basilisp_fn")
_FN_WITH_ATTRS_FN_NAME = _load_attr(f"{_RUNTIME_ALIAS}._with_attrs")
Expand Down Expand Up @@ -3559,9 +3561,24 @@ def _const_val_to_py_ast(
structures need to call into this function to generate Python AST nodes for
nested elements. For top-level :const Lisp AST nodes, see
`_const_node_to_py_ast`."""
raise ctx.GeneratorException(
f"No constant handler is defined for type {type(form)}"
)
try:
serialized = pickle.dumps(form)
except (pickle.PicklingError, RecursionError) as e:
# For types without custom "constant" handling code, we defer to pickle
# to generate a representation that can be reloaded from the generated
# byte code. There are a few cases where that may not be possible for one
# reason or another, in which case we'll fail here.
raise ctx.GeneratorException(
f"Unable to emit bytecode for generating a constant {type(form)}"
) from e
else:
return GeneratedPyAST(
node=ast.Call(
func=_BASILISP_LOAD_CONSTANT_NAME,
args=[ast.Constant(value=serialized)],
keywords=[],
),
)


def _collection_literal_to_py_ast(
Expand Down Expand Up @@ -3777,54 +3794,6 @@ def _const_set_to_py_ast(
)


@_const_val_to_py_ast.register(IRecord)
def _const_record_to_py_ast(
form: IRecord, ctx: GeneratorContext
) -> GeneratedPyAST[ast.expr]:
assert isinstance(form, IRecord) and isinstance(
form, ISeqable
), "IRecord types should also be ISeq"

tp = type(form)
assert hasattr(tp, "create") and callable(
tp.create
), "IRecord and IType must declare a .create class method"

form_seq = runtime.to_seq(form)
assert form_seq is not None, "IRecord types must be iterable"

# pylint: disable=no-member
keys: list[Optional[ast.expr]] = []
vals: list[ast.expr] = []
vals_deps: list[PyASTNode] = []
for k, v in form_seq:
assert isinstance(k, kw.Keyword), "Record key in seq must be keyword"
key_nodes = _kw_to_py_ast(k, ctx)
keys.append(key_nodes.node)
assert (
not key_nodes.dependencies
), "Simple AST generators must emit no dependencies"

val_nodes = _const_val_to_py_ast(v, ctx)
vals.append(val_nodes.node)
vals_deps.extend(val_nodes.dependencies)

return GeneratedPyAST(
node=ast.Call(
func=_load_attr(f"{tp.__qualname__}.create"),
args=[
ast.Call(
func=_NEW_MAP_FN_NAME,
args=[ast.Dict(keys=keys, values=vals)],
keywords=[],
)
],
keywords=[],
),
dependencies=vals_deps,
)


@_const_val_to_py_ast.register(llist.PersistentList)
@_const_val_to_py_ast.register(ISeq)
def _const_seq_to_py_ast(
Expand All @@ -3849,25 +3818,6 @@ def _const_seq_to_py_ast(
)


@_const_val_to_py_ast.register(IType)
def _const_type_to_py_ast(
form: IType, ctx: GeneratorContext
) -> GeneratedPyAST[ast.expr]:
tp = type(form)

ctor_args = []
ctor_arg_deps: list[PyASTNode] = []
for field in attr.fields(tp): # type: ignore[arg-type, misc, unused-ignore]
field_nodes = _const_val_to_py_ast(getattr(form, field.name, None), ctx)
ctor_args.append(field_nodes.node)
ctor_args.extend(field_nodes.dependencies) # type: ignore[arg-type]

return GeneratedPyAST(
node=ast.Call(func=_load_attr(tp.__qualname__), args=ctor_args, keywords=[]),
dependencies=ctor_arg_deps,
)


@_const_val_to_py_ast.register(vec.PersistentVector)
def _const_vec_to_py_ast(
form: vec.PersistentVector, ctx: GeneratorContext
Expand Down
13 changes: 13 additions & 0 deletions src/basilisp/lang/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import logging
import math
import numbers
import pickle # nosec B403
import platform
import re
import sys
Expand Down Expand Up @@ -2170,6 +2171,18 @@ def wrap_class(cls: type):
return wrap_class


def _load_constant(s: bytes) -> Any:
"""Load a compiler "constant" stored as a byte string as by Python's `pickle`
module.

Constant types without special handling are emitted to bytecode as a byte string
produced by `pickle.dumps`."""
try:
return pickle.loads(s) # nosec B301
except pickle.UnpicklingError as e:
raise RuntimeException("Unable to load constant value") from e


###############################
# Symbol and Alias Resolution #
###############################
Expand Down
Loading
Loading