Skip to content

Commit 05a50a8

Browse files
authored
Improve read expressions (#1217)
* Improve read expressions * Additional tests for ReadList * Fix multiline expressions in Read * Fix StreamPosition
1 parent 80928e0 commit 05a50a8

File tree

2 files changed

+65
-15
lines changed

2 files changed

+65
-15
lines changed

CHANGES.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@ New builtins
1919
Enhancements
2020
++++++++++++
2121

22-
* the Mathics version is checked for builtin modules at load time. A message is given when a builtin doesn't load.
22+
* The Mathics version is checked for builtin modules at load time. A message is given when a builtin doesn't load.
2323
* Automatic detection for the best strategy to numeric evaluation of constants.
2424
* ``FileNameJoin`` - implement ``OperatingSystem`` option
2525
* Mathics functions are accepted by ``Compile[]``. The return value or type will be
2626
``Compile[] and CompiledFunction[]`` every expression can have a compiled form,
2727
as a Python function.
2828
* ``Equal[]`` now compares complex against other numbers properly.
2929
* Improvements in handling products with infinite factors: ``0 Infinity``-> ``Indeterminate``, and ``expr Infinity``-> ``DirectedInfinite[expr]``
30+
* ``$Path`` is now Unprotected by default
31+
* Improving the reading of expressions from files.
3032
* ``StringSplit`` now accepts a list in the first argument.
3133
* ``SetDelayed`` now accepts several conditions imposed both at LHS as well as RHS.
3234

mathics/builtin/files.py

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@
2626

2727
from mathics.version import __version__ # noqa used in loading to check consistency.
2828

29+
from mathics_scanner.errors import IncompleteSyntaxError, InvalidSyntaxError
2930
from mathics_scanner import TranslateError
30-
from mathics.core.parser import MathicsFileLineFeeder
31+
from mathics.core.parser import MathicsFileLineFeeder, MathicsMultiLineFeeder, parse
32+
3133

3234
from mathics.core.expression import (
3335
BoxError,
@@ -394,7 +396,7 @@ class Path(Predefined):
394396
= ...
395397
"""
396398

397-
attributes = "Protected"
399+
attributes = ("Unprotected",)
398400
name = "$Path"
399401

400402
def evaluate(self, evaluation):
@@ -601,6 +603,12 @@ class Read(Builtin):
601603
602604
#> Quiet[Read[str, {Real}]]
603605
= Read[InputStream[String, ...], {Real}]
606+
607+
Multiple lines:
608+
>> str = StringToStream["\\"Tengo una\\nvaca lechera.\\""]; Read[str]
609+
= Tengo una
610+
. vaca lechera.
611+
604612
"""
605613

606614
messages = {
@@ -713,8 +721,16 @@ def apply(self, channel, types, evaluation, options):
713721
return
714722

715723
# Wrap types in a list (if it isn't already one)
716-
if not types.has_form("List", None):
717-
types = Expression("List", types)
724+
if types.has_form("List", None):
725+
types = types._leaves
726+
else:
727+
types = (types,)
728+
729+
types = (
730+
typ._leaves[0] if typ.get_head_name() == "System`Hold" else typ
731+
for typ in types
732+
)
733+
types = Expression("List", *types)
718734

719735
READ_TYPES = [
720736
Symbol(k)
@@ -760,19 +776,38 @@ def reader(stream, word_separators, accepted=None):
760776

761777
if tmp == "":
762778
if word == "":
763-
raise EOFError
764-
yield word
779+
pos = stream.tell()
780+
newchar = stream.read(1)
781+
if pos == stream.tell():
782+
raise EOFError
783+
else:
784+
if newchar:
785+
word = newchar
786+
continue
787+
else:
788+
yield word
789+
continue
790+
last_word = word
791+
word = ""
792+
yield last_word
793+
break
765794

766795
if tmp in word_separators:
767796
if word == "":
768-
break
797+
continue
769798
if stream.seekable():
770-
# stream.seek(-1, 1) #Python3
799+
# stream.seek(-1, 1) #Python3
771800
stream.seek(stream.tell() - 1)
772-
yield word
801+
last_word = word
802+
word = ""
803+
yield last_word
804+
break
773805

774806
if accepted is not None and tmp not in accepted:
775-
yield word
807+
last_word = word
808+
word = ""
809+
yield last_word
810+
break
776811

777812
word += tmp
778813

@@ -802,13 +837,27 @@ def reader(stream, word_separators, accepted=None):
802837
result.append(tmp)
803838
elif typ == Symbol("Expression"):
804839
tmp = next(read_record)
805-
expr = evaluation.parse(tmp)
840+
while True:
841+
try:
842+
feeder = MathicsMultiLineFeeder(tmp)
843+
expr = parse(evaluation.definitions, feeder)
844+
break
845+
except (IncompleteSyntaxError, InvalidSyntaxError) as e:
846+
try:
847+
nextline = next(read_record)
848+
tmp = tmp + "\n" + nextline
849+
except EOFError:
850+
expr = Symbol("EndOfFile")
851+
break
852+
806853
if expr is None:
807854
evaluation.message(
808855
"Read", "readt", tmp, Expression("InputSteam", name, n)
809856
)
810857
return SymbolFailed
811-
result.append(tmp)
858+
else:
859+
result.append(expr)
860+
812861
elif typ == Symbol("Number"):
813862
tmp = next(read_number)
814863
try:
@@ -2180,7 +2229,6 @@ class Get(PrefixOperator):
21802229

21812230
def apply(self, path, evaluation, options):
21822231
"Get[path_String, OptionsPattern[Get]]"
2183-
from mathics.core.parser import parse
21842232

21852233
def check_options(options):
21862234
# Options
@@ -2942,7 +2990,7 @@ class ReadList(Read):
29422990
>> ReadList[StringToStream["a 1 b 2"], {Word, Number}]
29432991
= {{a, 1}, {b, 2}}
29442992
2945-
>> str = StringToStream["abc123"];
2993+
>> str = StringToStream["\\"abc123\\""];
29462994
>> ReadList[str]
29472995
= {abc123}
29482996
>> InputForm[%]

0 commit comments

Comments
 (0)