Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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 taht 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