Skip to content

Commit

Permalink
Report Read noopen error...
Browse files Browse the repository at this point in the history
and move parse Read ReadList options to eval.
  • Loading branch information
rocky committed Sep 24, 2024
1 parent d6248b5 commit 22d79c1
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 77 deletions.
91 changes: 19 additions & 72 deletions mathics/builtin/files_io/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,7 @@ class Read(Builtin):

messages = {
"openx": "`1` is not open.",
"noopen": "Cannot open `1`.",
"readf": "`1` is not a valid format specification.",
"readn": "Invalid real number found when reading from `1`.",
"readt": "Invalid input found when reading `1` from `2`.",
Expand All @@ -819,75 +820,15 @@ class Read(Builtin):
}
summary_text = "read an object of the specified type from a stream"

def check_options(self, options) -> dict:
# Options
# TODO Proper error messages

result = {}
keys = list(options.keys())

# AnchoredSearch
if "System`AnchoredSearch" in keys:
anchored_search = options["System`AnchoredSearch"].to_python()
assert anchored_search in [True, False]
result["AnchoredSearch"] = anchored_search

# IgnoreCase
if "System`IgnoreCase" in keys:
ignore_case = options["System`IgnoreCase"].to_python()
assert ignore_case in [True, False]
result["IgnoreCase"] = ignore_case

# WordSearch
if "System`WordSearch" in keys:
word_search = options["System`WordSearch"].to_python()
assert word_search in [True, False]
result["WordSearch"] = word_search

# RecordSeparators
if "System`RecordSeparators" in keys:
record_separators = options["System`RecordSeparators"].to_python()
assert isinstance(record_separators, list)
assert all(
isinstance(s, str) and s[0] == s[-1] == '"' for s in record_separators
)
record_separators = [s[1:-1] for s in record_separators]
result["RecordSeparators"] = record_separators

# WordSeparators
if "System`WordSeparators" in keys:
word_separators = options["System`WordSeparators"].to_python()
assert isinstance(word_separators, list)
assert all(
isinstance(s, str) and s[0] == s[-1] == '"' for s in word_separators
)
word_separators = [s[1:-1] for s in word_separators]
result["WordSeparators"] = word_separators

# NullRecords
if "System`NullRecords" in keys:
null_records = options["System`NullRecords"].to_python()
assert null_records in [True, False]
result["NullRecords"] = null_records

# NullWords
if "System`NullWords" in keys:
null_words = options["System`NullWords"].to_python()
assert null_words in [True, False]
result["NullWords"] = null_words

# TokenWords
if "System`TokenWords" in keys:
token_words = options["System`TokenWords"].to_python()
assert token_words == []
result["TokenWords"] = token_words

return result

def eval(self, channel, types, evaluation: Evaluation, options: dict):
"Read[channel_, types_, OptionsPattern[Read]]"

name, n, stream = read_name_and_stream_from_channel(channel, evaluation)
try:
name, n, stream = read_name_and_stream_from_channel(channel, evaluation)
except IOError as e:
evaluation.message("Read", "noopen", str(e))
return SymbolFailed

if name is None:
return

Expand Down Expand Up @@ -927,17 +868,23 @@ def eval(self, channel, types, evaluation: Evaluation, options: dict):

result = []

read_word = read_from_stream(stream, word_separators, evaluation.message)
read_record = read_from_stream(stream, record_separators, evaluation.message)
read_word = read_from_stream(
stream, word_separators, token_words, evaluation.message
)
read_record = read_from_stream(
stream, record_separators, token_words, evaluation.message
)
read_number = read_from_stream(
stream,
word_separators + record_separators,
token_words,
evaluation.message,
["+", "-", "."] + [str(i) for i in range(10)],
)
read_real = read_from_stream(
stream,
word_separators + record_separators,
token_words,
evaluation.message,
["+", "-", ".", "e", "E", "^", "*"] + [str(i) for i in range(10)],
)
Expand Down Expand Up @@ -1103,7 +1050,7 @@ def eval(self, channel, types, evaluation: Evaluation, options: dict):

# Options
# TODO: Implement extra options
# py_options = self.check_options(options)
# py_options = parse_read_options(options)
# null_records = py_options['NullRecords']
# null_words = py_options['NullWords']
# record_separators = py_options['RecordSeparators']
Expand All @@ -1130,7 +1077,7 @@ def eval_n(self, channel, types, n: Integer, evaluation: Evaluation, options: di

# Options
# TODO: Implement extra options
# py_options = self.check_options(options)
# py_options = parse_read_options(options)
# null_records = py_options['NullRecords']
# null_words = py_options['NullWords']
# record_separators = py_options['RecordSeparators']
Expand Down Expand Up @@ -1337,7 +1284,7 @@ def eval(self, name, n, types, m, evaluation: Evaluation, options: dict):

# Options
# TODO Implement extra options
# py_options = self.check_options(options)
# py_options = parse_read_options(options)
# null_records = py_options['NullRecords']
# null_words = py_options['NullWords']
# record_separators = py_options['RecordSeparators']
Expand Down Expand Up @@ -1399,7 +1346,7 @@ def eval(self, name, n, text, evaluation: Evaluation, options: dict):

# Options
# TODO Implement extra options
# py_options = self.check_options(options)
# py_options = parse_read_options(options)
# anchored_search = py_options['AnchoredSearch']
# ignore_case = py_options['IgnoreCase']
# word_search = py_options['WordSearch']
Expand Down
92 changes: 87 additions & 5 deletions mathics/eval/files_io/read.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""

import io
from typing import Optional, Tuple
from typing import Callable, Optional, Tuple

from mathics.builtin.atomic.strings import to_python_encoding
from mathics.core.atoms import Integer, String
Expand Down Expand Up @@ -77,7 +77,7 @@ def __enter__(self, is_temporary_file=False):
if path is None and self.mode in ["w", "a", "wb", "ab"]:
path = self.name
if path is None:
raise IOError
raise IOError(self.name)

# Open the file
self.fp = io.open(path, self.mode, encoding=self.encoding)
Expand Down Expand Up @@ -122,6 +122,73 @@ def channel_to_stream(channel, mode="r"):
return None


def parse_read_options(options) -> dict:
"""
Parses and checks Read[] or ReadList[] options
"""
# Options
# TODO Proper error messages

result = {}
keys = list(options.keys())

# AnchoredSearch
if "System`AnchoredSearch" in keys:
anchored_search = options["System`AnchoredSearch"].to_python()
assert anchored_search in [True, False]
result["AnchoredSearch"] = anchored_search

# IgnoreCase
if "System`IgnoreCase" in keys:
ignore_case = options["System`IgnoreCase"].to_python()
assert ignore_case in [True, False]
result["IgnoreCase"] = ignore_case

# WordSearch
if "System`WordSearch" in keys:
word_search = options["System`WordSearch"].to_python()
assert word_search in [True, False]
result["WordSearch"] = word_search

# RecordSeparators
if "System`RecordSeparators" in keys:
record_separators = options["System`RecordSeparators"].to_python()
assert isinstance(record_separators, list)
assert all(
isinstance(s, str) and s[0] == s[-1] == '"' for s in record_separators
)
record_separators = [s[1:-1] for s in record_separators]
result["RecordSeparators"] = record_separators

# WordSeparators
if "System`WordSeparators" in keys:
word_separators = options["System`WordSeparators"].to_python()
assert isinstance(word_separators, list)
assert all(isinstance(s, str) and s[0] == s[-1] == '"' for s in word_separators)
word_separators = [s[1:-1] for s in word_separators]
result["WordSeparators"] = word_separators

# NullRecords
if "System`NullRecords" in keys:
null_records = options["System`NullRecords"].to_python()
assert null_records in [True, False]
result["NullRecords"] = null_records

# NullWords
if "System`NullWords" in keys:
null_words = options["System`NullWords"].to_python()
assert null_words in [True, False]
result["NullWords"] = null_words

# TokenWords
if "System`TokenWords" in keys:
token_words = options["System`TokenWords"].to_python()
assert token_words == []
result["TokenWords"] = token_words

return result


def close_stream(stream: Stream, stream_number: int):
"""
Close stream: `stream` and delete it from the list of streams we manage.
Expand Down Expand Up @@ -242,6 +309,7 @@ def read_check_options(options: dict, evaluation: Evaluation) -> Optional[dict]:
if not (isinstance(token_words, list) or isinstance(token_words, String)):
evaluation.message("ReadList", "opstl", token_words)
return None
# from trepan.api import debug; debug()
result["TokenWords"] = token_words

return result
Expand All @@ -265,13 +333,17 @@ def read_get_separators(
return record_separators, token_words, word_separators


def read_from_stream(stream, word_separators, msgfn, accepted=None):
def read_from_stream(
stream, word_separators: list, token_words: list, msgfn: Callable, accepted=None
):
"""
This is a generator that returns "words" from stream deliminated by
"word_separators"
"word_separators" or "token_words".
"""
# from trepan.api import debug; debug()
while True:
word = ""
some_token_word_prefix = ""
while True:
try:
tmp = stream.io.read(1)
Expand Down Expand Up @@ -304,15 +376,25 @@ def read_from_stream(stream, word_separators, msgfn, accepted=None):
continue
if stream.io.seekable():
stream.io.seek(stream.io.tell() - 1)
word += some_token_word_prefix
last_word = word
word = ""
some_token_word_prefix = ""
yield last_word
break

if accepted is not None and tmp not in accepted:
word += some_token_word_prefix
last_word = word
word = ""
some_token_word_prefix = ""
yield last_word
break

word += tmp
some_token_word_prefix += tmp
for token_word in token_words:
if token_word == some_token_word_prefix:
continue
else:
word += some_token_word_prefix
some_token_word_prefix = ""

0 comments on commit 22d79c1

Please sign in to comment.