Skip to content

Commit

Permalink
Fix references
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisrink10 committed Jul 27, 2019
1 parent 1abe687 commit 2f8d45e
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 228 deletions.
241 changes: 20 additions & 221 deletions src/basilisp/lang/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import uuid
from datetime import datetime
from fractions import Fraction
from itertools import chain
from typing import (
Any,
Callable,
Expand All @@ -18,7 +17,6 @@
MutableMapping,
Optional,
Pattern,
Set,
Tuple,
TypeVar,
Union,
Expand All @@ -35,20 +33,11 @@
import basilisp.lang.util as langutil
import basilisp.lang.vector as vector
import basilisp.walker as walk
from basilisp.lang.interfaces import (
ILispObject,
ILookup,
IMeta,
IPersistentList,
IPersistentSet,
IRecord,
IType,
)
from basilisp.lang.obj import seq_lrepr as _seq_lrepr
from basilisp.lang.interfaces import IMeta, IRecord, IType
from basilisp.lang.runtime import Namespace, Var
from basilisp.lang.typing import IterableLispForm, LispForm, ReaderForm
from basilisp.lang.util import munge
from basilisp.util import Maybe, partition
from basilisp.util import Maybe

ns_name_chars = re.compile(r"\w|-|\+|\*|\?|/|\=|\\|!|&|%|>|<|\$|\.")
alphanumeric_chars = re.compile(r"\w")
Expand All @@ -68,14 +57,6 @@
READER_LINE_KW = keyword.keyword("line", ns="basilisp.lang.reader")
READER_COL_KW = keyword.keyword("col", ns="basilisp.lang.reader")

READER_COND_BASILISP_FEATURE_KW = keyword.keyword("lpy")
READER_COND_DEFAULT_FEATURE_KW = keyword.keyword("default")
READER_COND_DEFAULT_FEATURE_SET = lset.s(
READER_COND_BASILISP_FEATURE_KW, READER_COND_DEFAULT_FEATURE_KW
)
READER_COND_FORM_KW = keyword.keyword("form")
READER_COND_SPLICING_KW = keyword.keyword("splicing?")

_AMPERSAND = symbol.symbol("&")
_FN = symbol.symbol("fn*")
_INTEROP_CALL = symbol.symbol(".")
Expand Down Expand Up @@ -234,8 +215,6 @@ class ReaderContext:

__slots__ = (
"_data_readers",
"_features",
"_process_reader_cond",
"_reader",
"_resolve",
"_in_anon_fn",
Expand All @@ -250,8 +229,6 @@ def __init__(
resolver: Resolver = None,
data_readers: DataReaders = None,
eof: Any = None,
features: IPersistentSet[keyword.Keyword] = READER_COND_DEFAULT_FEATURE_SET,
process_reader_cond: bool = True,
) -> None:
data_readers = Maybe(data_readers).or_else_get(lmap.Map.empty())
for entry in data_readers:
Expand All @@ -264,8 +241,6 @@ def __init__(
lambda l, r: l, # Do not allow callers to overwrite existing builtin readers
data_readers,
)
self._features = features
self._process_reader_cond = process_reader_cond
self._reader = reader
self._resolve = Maybe(resolver).or_else_get(lambda x: x)
self._in_anon_fn: Deque[bool] = collections.deque([])
Expand All @@ -281,14 +256,6 @@ def data_readers(self) -> lmap.Map:
def eof(self) -> Any:
return self._eof

@property
def reader_features(self) -> IPersistentSet[keyword.Keyword]:
return self._features

@property
def should_process_reader_cond(self) -> bool:
return self._process_reader_cond

@property
def reader(self) -> StreamReader:
return self._reader
Expand Down Expand Up @@ -335,72 +302,6 @@ def is_syntax_quoted(self) -> bool:
return False


class ReaderConditional(ILookup[keyword.Keyword, ReaderForm], ILispObject):
FEATURE_NOT_PRESENT = object()

__slots__ = ("_form", "_feature_vec", "_is_splicing")

def __init__(
self,
form: IPersistentList[Tuple[keyword.Keyword, ReaderForm]],
is_splicing: bool = False,
):
self._form = form
self._feature_vec = self._compile_feature_vec(form)
self._is_splicing = is_splicing

@staticmethod
def _compile_feature_vec(form: IPersistentList[Tuple[keyword.Keyword, ReaderForm]]):
found_features: Set[keyword.Keyword] = set()
feature_list: List[Tuple[keyword.Keyword, ReaderForm]] = []

try:
for k, v in partition(form, 2):
if not isinstance(k, keyword.Keyword):
raise SyntaxError(
f"Reader conditional features must be keywords, not {type(k)}"
)
if k in found_features:
raise SyntaxError(
f"Duplicate feature '{k}' in reader conditional literal"
)
found_features.add(k)
feature_list.append((k, v))
except ValueError:
raise SyntaxError(
"Reader conditionals must contain an even number of forms"
)

return vector.vector(feature_list)

def val_at(
self, k: keyword.Keyword, default: Optional[ReaderForm] = None
) -> Optional[ReaderForm]:
return {
READER_COND_FORM_KW: self._form,
READER_COND_SPLICING_KW: self._is_splicing,
}.get(k)

@property
def is_splicing(self):
return self._is_splicing

def select_feature(
self, features: IPersistentSet[keyword.Keyword]
) -> Union[ReaderForm, object]:
for k, form in self._feature_vec:
if k in features:
return form
return self.FEATURE_NOT_PRESENT

def _lrepr(self, **kwargs) -> str:
return _seq_lrepr(
chain.from_iterable(self._feature_vec),
"#?@" if self.is_splicing else "#?",
")",
)


EOF = "EOF"


Expand Down Expand Up @@ -489,23 +390,7 @@ def _read_coll(
elem = _read_next(ctx)
if elem is COMMENT:
continue
if _should_splice_reader_conditional(ctx, elem):
selected_feature = elem.select_feature(ctx.reader_features)
if selected_feature is ReaderConditional.FEATURE_NOT_PRESENT:
continue
elif isinstance(selected_feature, vector.Vector):
coll.extend(selected_feature)
else:
raise SyntaxError(
"Expecting Vector for splicing reader conditional "
f"form; got {type(selected_feature)}"
)
else:
assert (
not isinstance(elem, ReaderConditional)
or not ctx.should_process_reader_cond
), "Reader conditionals must be processed if specified"
coll.append(elem)
coll.append(elem)


def _consume_whitespace(reader: StreamReader) -> None:
Expand Down Expand Up @@ -544,53 +429,32 @@ def set_if_valid(s: Collection) -> lset.Set:
return _read_coll(ctx, set_if_valid, "}", "set")


def __read_map_elems(ctx: ReaderContext) -> Iterable[ReaderForm]:
"""Return an iterable of map contents, potentially splicing in values from
reader conditionals."""
reader = ctx.reader
while True:
if reader.peek() == "}":
reader.next_token()
return
v = _read_next(ctx)
if v is COMMENT:
continue
elif _should_splice_reader_conditional(ctx, v):
assert isinstance(v, ReaderConditional)
selected_feature = v.select_feature(ctx.reader_features)
if selected_feature is ReaderConditional.FEATURE_NOT_PRESENT:
continue
elif isinstance(selected_feature, vector.Vector):
yield from selected_feature
else:
raise SyntaxError(
"Expecting Vector for splicing reader conditional "
f"form; got {type(selected_feature)}"
)
else:
assert (
not isinstance(v, ReaderConditional)
or not ctx.should_process_reader_cond
), "Reader conditionals must be processed if specified"
yield v


@_with_loc
def _read_map(ctx: ReaderContext) -> lmap.Map:
"""Return a map from the input stream."""
reader = ctx.reader
start = reader.advance()
assert start == "{"
d: MutableMapping[Any, Any] = {}
try:
for k, v in partition(list(__read_map_elems(ctx)), 2):
while True:
if reader.peek() == "}":
reader.next_token()
break
k = _read_next(ctx)
if k is COMMENT:
continue
while True:
if reader.peek() == "}":
raise SyntaxError("Unexpected token '}'; expected map value")
v = _read_next(ctx)
if v is COMMENT:
continue
if k in d:
raise SyntaxError(f"Duplicate key '{k}' in map literal")
d[k] = v
except ValueError:
raise SyntaxError("Unexpected token '}'; expected map value")
else:
return lmap.map(d)
break
d[k] = v

return lmap.map(d)


# Due to some ambiguities that arise in parsing symbols, numbers, and the
Expand Down Expand Up @@ -1060,65 +924,6 @@ def _read_regex(ctx: ReaderContext) -> Pattern:
raise SyntaxError(f"Unrecognized regex pattern syntax: {s}")


def _should_splice_reader_conditional(ctx: ReaderContext, form: ReaderForm) -> bool:
"""Return True if and only if form is a ReaderConditional which should be spliced
into a surrounding collection context."""
return (
isinstance(form, ReaderConditional)
and ctx.should_process_reader_cond
and form.is_splicing
)


def _read_reader_conditional_preserving(ctx: ReaderContext) -> ReaderConditional:
"""Read a reader conditional form and return the unprocessed reader
conditional object."""
reader = ctx.reader
start = reader.advance()
assert start == "?"
token = reader.peek()

if token == "@":
is_splicing = True
ctx.reader.advance()
elif token == "(":
is_splicing = False
else:
raise SyntaxError(
f"Unexpected token '{token}'; expected opening "
"'(' for reader conditional"
)

open_token = reader.advance()
assert open_token == "("
feature_list = _read_coll(ctx, llist.list, ")", "reader conditional")
assert isinstance(feature_list, llist.List)
return ReaderConditional(feature_list, is_splicing=is_splicing)


def _read_reader_conditional(
ctx: ReaderContext
) -> Union[ReaderForm, ReaderConditional]:
"""Read a reader conditional form and either return it or process it and
return the resulting form.
If the reader is not set to process the reader conditional, it will always
be returned as a ReaderConditional object.
If the reader is set to process reader conditionals, only non-splicing reader
conditionals are processed here. If no matching feature is found in a
non-splicing reader conditional, a comment will be emitted (which is ultimately
discarded downstream in the reader).
Splicing reader conditionals are processed in the respective collection readers."""
reader_cond = _read_reader_conditional_preserving(ctx)
if ctx.should_process_reader_cond and not reader_cond.is_splicing:
form = reader_cond.select_feature(ctx.reader_features)
return COMMENT if form is ReaderConditional.FEATURE_NOT_PRESENT else form
else:
return reader_cond


def _load_record_or_type(s: symbol.Symbol, v: LispReaderForm) -> Union[IRecord, IType]:
"""Attempt to load the constructor named by `s` and construct a new
record or type instance from the vector or map following name."""
Expand Down Expand Up @@ -1175,8 +980,6 @@ def _read_reader_macro(ctx: ReaderContext) -> LispReaderForm:
ctx.reader.advance()
_read_next(ctx) # Ignore the entire next form
return COMMENT
elif token == "?":
return _read_reader_conditional(ctx)
elif ns_name_chars.match(token):
s = _read_sym(ctx)
assert isinstance(s, symbol.Symbol)
Expand Down Expand Up @@ -1293,10 +1096,6 @@ def read(
return
if expr is COMMENT or isinstance(expr, Comment):
continue
assert (
not isinstance(expr, ReaderConditional)
or not ctx.should_process_reader_cond
), "Reader conditionals must be processed if specified"
yield expr


Expand Down
3 changes: 1 addition & 2 deletions tests/basilisp/compiler_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import basilisp.lang.runtime as runtime
import basilisp.lang.set as lset
import basilisp.lang.symbol as sym
import basilisp.lang.vector
import basilisp.lang.vector as vec
from basilisp.lang.interfaces import IType
from basilisp.lang.runtime import Var
Expand Down Expand Up @@ -2840,7 +2839,7 @@ def test_namespaced_sym_cannot_resolve(self, ns: runtime.Namespace):
lcompile("other.ns/name")

def test_nested_namespaced_sym_will_resolve(self, ns: runtime.Namespace):
assert basilisp.lang.vector.MapEntry.of is lcompile(
assert lmap.MapEntry.of is lcompile(
"basilisp.lang.map.MapEntry/of"
)

Expand Down
3 changes: 1 addition & 2 deletions tests/basilisp/core_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import basilisp.lang.runtime as runtime
import basilisp.lang.set as lset
import basilisp.lang.symbol as sym
import basilisp.lang.vector
import basilisp.lang.vector as vec
from basilisp.lang.exception import ExceptionInfo
from basilisp.lang.interfaces import IExceptionInfo
Expand Down Expand Up @@ -625,7 +624,7 @@ def test_is_qualified_symbol(self, v):


def test_is_map_entry():
assert True is core.map_entry__Q__(basilisp.lang.vector.MapEntry.of("a", "b"))
assert True is core.map_entry__Q__(lmap.MapEntry.of("a", "b"))
assert False is core.map_entry__Q__(vec.Vector.empty())
assert False is core.map_entry__Q__(vec.v("a", "b"))
assert False is core.map_entry__Q__(vec.v("a", "b", "c"))
Expand Down

0 comments on commit 2f8d45e

Please sign in to comment.