1# -*- coding: utf-8 -*- 2# cython: language_level=3 3 4""" 5File and Stream Operations 6""" 7 8import io 9import math 10import mpmath 11import os 12import struct 13import sympy 14import tempfile 15 16from io import BytesIO 17import os.path as osp 18from itertools import chain 19 20from mathics.version import __version__ # noqa used in loading to check consistency. 21 22from mathics_scanner.errors import IncompleteSyntaxError, InvalidSyntaxError 23from mathics_scanner import TranslateError 24from mathics.core.parser import MathicsFileLineFeeder, MathicsMultiLineFeeder, parse 25 26 27from mathics.core.expression import ( 28 BoxError, 29 Complex, 30 BaseExpression, 31 Expression, 32 Integer, 33 MachineReal, 34 Real, 35 String, 36 Symbol, 37 SymbolFailed, 38 SymbolNull, 39 SymbolTrue, 40 from_mpmath, 41 from_python, 42) 43from mathics.core.numbers import dps 44from mathics.core.streams import ( 45 Stream, 46 path_search, 47 stream_manager, 48) 49import mathics 50from mathics.builtin.base import Builtin, Predefined, BinaryOperator, PrefixOperator 51from mathics.builtin.strings import to_python_encoding 52from mathics.builtin.base import MessageException 53 54INITIAL_DIR = os.getcwd() 55DIRECTORY_STACK = [INITIAL_DIR] 56 57INPUT_VAR = "" 58INPUTFILE_VAR = "" 59 60TMP_DIR = tempfile.gettempdir() 61SymbolPath = Symbol("$Path") 62 63def _channel_to_stream(channel, mode="r"): 64 if isinstance(channel, String): 65 name = channel.get_string_value() 66 opener = mathics_open(name, mode) 67 opener.__enter__() 68 n = opener.n 69 if mode in ["r", "rb"]: 70 head = "InputStream" 71 elif mode in ["w", "a", "wb", "ab"]: 72 head = "OutputStream" 73 else: 74 raise ValueError(f"Unknown format {mode}") 75 return Expression(head, channel, Integer(n)) 76 elif channel.has_form("InputStream", 2): 77 return channel 78 elif channel.has_form("OutputStream", 2): 79 return channel 80 else: 81 return None 82 83 84class mathics_open(Stream): 85 def __init__(self, name, mode="r", encoding=None): 86 if encoding is not None: 87 encoding = to_python_encoding(encoding) 88 if "b" in mode: 89 # We should not specify an encoding for a binary mode 90 encoding = None 91 elif encoding is None: 92 raise MessageException("General", "charcode", self.encoding) 93 self.encoding = encoding 94 super().__init__(name, mode, self.encoding) 95 self.old_inputfile_var = None # Set in __enter__ and __exit__ 96 97 def __enter__(self): 98 # find path 99 path = path_search(self.name) 100 if path is None and self.mode in ["w", "a", "wb", "ab"]: 101 path = self.name 102 if path is None: 103 raise IOError 104 105 # open the stream 106 fp = io.open(path, self.mode, encoding=self.encoding) 107 global INPUTFILE_VAR 108 INPUTFILE_VAR = osp.abspath(path) 109 110 stream_manager.add( 111 name=path, 112 mode=self.mode, 113 encoding=self.encoding, 114 io=fp, 115 num=stream_manager.next, 116 ) 117 return fp 118 119 def __exit__(self, type, value, traceback): 120 global INPUTFILE_VAR 121 INPUTFILE_VAR = self.old_inputfile_var or "" 122 super().__exit__(type, value, traceback) 123 124 125class Input(Predefined): 126 """ 127 <dl> 128 <dt>'$Input' 129 <dd>is the name of the stream from which input is currently being read. 130 </dl> 131 132 >> $Input 133 = #<--# 134 """ 135 136 attributes = ("Protected", "ReadProtected") 137 name = "$Input" 138 139 def evaluate(self, evaluation): 140 global INPUT_VAR 141 return String(INPUT_VAR) 142 143 144class InputFileName(Predefined): 145 """ 146 <dl> 147 <dt>'$InputFileName' 148 <dd>is the name of the file from which input is currently being read. 149 </dl> 150 151 While in interactive mode, '$InputFileName' is "". 152 X> $InputFileName 153 """ 154 155 name = "$InputFileName" 156 157 def evaluate(self, evaluation): 158 global INPUTFILE_VAR 159 return String(INPUTFILE_VAR) 160 161 162class EndOfFile(Builtin): 163 """ 164 <dl> 165 <dt>'EndOfFile' 166 <dd>is returned by 'Read' when the end of an input stream is reached. 167 </dl> 168 """ 169 170SymbolEndOfFile = Symbol("EndOfFile") 171 172 173# TODO: Improve docs for these Read[] arguments. 174class Byte(Builtin): 175 """ 176 <dl> 177 <dt>'Byte' 178 <dd>is a data type for 'Read'. 179 </dl> 180 """ 181 182 183class Character(Builtin): 184 """ 185 <dl> 186 <dt>'Character' 187 <dd>is a data type for 'Read'. 188 </dl> 189 """ 190 191 192class Expression_(Builtin): 193 """ 194 <dl> 195 <dt>'Expression' 196 <dd>is a data type for 'Read'. 197 </dl> 198 """ 199 200 name = "Expression" 201 202 203class Number_(Builtin): 204 """ 205 <dl> 206 <dt>'Number' 207 <dd>is a data type for 'Read'. 208 </dl> 209 """ 210 211 name = "Number" 212 213 214class Record(Builtin): 215 """ 216 <dl> 217 <dt>'Record' 218 <dd>is a data type for 'Read'. 219 </dl> 220 """ 221 222 223class Word(Builtin): 224 """ 225 <dl> 226 <dt>'Word' 227 <dd>is a data type for 'Read'. 228 </dl> 229 """ 230 231 232class Read(Builtin): 233 """ 234 <dl> 235 <dt>'Read[$stream$]' 236 <dd>reads the input stream and returns one expression. 237 238 <dt>'Read[$stream$, $type$]' 239 <dd>reads the input stream and returns an object of the given type. 240 241 <dt>'Read[$stream$, $type$]' 242 <dd>reads the input stream and returns an object of the given type. 243 244 <dt>'Read[$stream$, Hold[Expression]]' 245 <dd>reads the input stream for an Expression and puts it inside 'Hold'. 246 247 </dl> 248 $type$ is one of: 249 <ul> 250 <li>Byte 251 <li>Character 252 <li>Expression 253 <li>HoldExpression 254 <li>Number 255 <li>Real 256 <li>Record 257 <li>String 258 <li>Word 259 </ul> 260 261 ## Malformed InputString 262 #> Read[InputStream[String], {Word, Number}] 263 = Read[InputStream[String], {Word, Number}] 264 265 ## Correctly formed InputString but not open 266 #> Read[InputStream[String, -1], {Word, Number}] 267 : InputStream[String, -1] is not open. 268 = Read[InputStream[String, -1], {Word, Number}] 269 270 ## Reading Strings 271 >> stream = StringToStream["abc123"]; 272 >> Read[stream, String] 273 = abc123 274 #> Read[stream, String] 275 = EndOfFile 276 #> Close[stream]; 277 278 ## Reading Words 279 >> stream = StringToStream["abc 123"]; 280 >> Read[stream, Word] 281 = abc 282 >> Read[stream, Word] 283 = 123 284 #> Read[stream, Word] 285 = EndOfFile 286 #> Close[stream]; 287 #> stream = StringToStream[""]; 288 #> Read[stream, Word] 289 = EndOfFile 290 #> Read[stream, Word] 291 = EndOfFile 292 #> Close[stream]; 293 294 ## Number 295 >> stream = StringToStream["123, 4"]; 296 >> Read[stream, Number] 297 = 123 298 >> Read[stream, Number] 299 = 4 300 #> Read[stream, Number] 301 = EndOfFile 302 #> Close[stream]; 303 #> stream = StringToStream["123xyz 321"]; 304 #> Read[stream, Number] 305 = 123 306 #> Quiet[Read[stream, Number]] 307 = $Failed 308 309 ## Real 310 #> stream = StringToStream["123, 4abc"]; 311 #> Read[stream, Real] 312 = 123. 313 #> Read[stream, Real] 314 = 4. 315 #> Quiet[Read[stream, Number]] 316 = $Failed 317 318 #> Close[stream]; 319 #> stream = StringToStream["1.523E-19"]; Read[stream, Real] 320 = 1.523*^-19 321 #> Close[stream]; 322 #> stream = StringToStream["-1.523e19"]; Read[stream, Real] 323 = -1.523*^19 324 #> Close[stream]; 325 #> stream = StringToStream["3*^10"]; Read[stream, Real] 326 = 3.*^10 327 #> Close[stream]; 328 #> stream = StringToStream["3.*^10"]; Read[stream, Real] 329 = 3.*^10 330 #> Close[stream]; 331 332 ## Expression 333 #> stream = StringToStream["x + y Sin[z]"]; Read[stream, Expression] 334 = x + y Sin[z] 335 #> Close[stream]; 336 ## #> stream = Quiet[StringToStream["Sin[1 123"]; Read[stream, Expression]] 337 ## = $Failed 338 339 ## HoldExpression: 340 >> stream = StringToStream["2+2\\n2+3"]; 341 342 'Read' with a 'Hold[Expression]' returns the expression it reads unevaluated so it can be later inspected and evaluated: 343 344 >> Read[stream, Hold[Expression]] 345 = Hold[2 + 2] 346 347 >> Read[stream, Expression] 348 = 5 349 >> Close[stream]; 350 351 Reading a comment however will return the empy list: 352 >> stream = StringToStream["(* ::Package:: *)"]; 353 354 >> Read[stream, Hold[Expression]] 355 = {} 356 357 >> Close[stream]; 358 359 ## Multiple types 360 >> stream = StringToStream["123 abc"]; 361 >> Read[stream, {Number, Word}] 362 = {123, abc} 363 #> Read[stream, {Number, Word}] 364 = EndOfFile 365 #> Close[stream]; 366 367 #> stream = StringToStream["123 abc"]; 368 #> Quiet[Read[stream, {Word, Number}]] 369 = $Failed 370 #> Close[stream]; 371 372 #> stream = StringToStream["123 123"]; Read[stream, {Real, Number}] 373 = {123., 123} 374 #> Close[stream]; 375 376 #> Quiet[Read[stream, {Real}]] 377 = Read[InputStream[String, ...], {Real}] 378 379 Multiple lines: 380 >> stream = StringToStream["\\"Tengo una\\nvaca lechera.\\""]; Read[stream] 381 = Tengo una 382 . vaca lechera. 383 384 """ 385 386 messages = { 387 "openx": "`1` is not open.", 388 "readf": "`1` is not a valid format specification.", 389 "readn": "Invalid real number found when reading from `1`.", 390 "readt": "Invalid input found when reading `1` from `2`.", 391 "intnm": ( 392 "Non-negative machine-sized integer expected at " "position 3 in `1`." 393 ), 394 } 395 396 rules = { 397 "Read[stream_]": "Read[stream, Expression]", 398 } 399 400 options = { 401 "NullRecords": "False", 402 "NullWords": "False", 403 "RecordSeparators": '{"\r\n", "\n", "\r"}', 404 "TokenWords": "{}", 405 "WordSeparators": '{" ", "\t"}', 406 } 407 408 attributes = "Protected" 409 410 def check_options(self, options): 411 # Options 412 # TODO Proper error messages 413 414 result = {} 415 keys = list(options.keys()) 416 417 # AnchoredSearch 418 if "System`AnchoredSearch" in keys: 419 anchored_search = options["System`AnchoredSearch"].to_python() 420 assert anchored_search in [True, False] 421 result["AnchoredSearch"] = anchored_search 422 423 # IgnoreCase 424 if "System`IgnoreCase" in keys: 425 ignore_case = options["System`IgnoreCase"].to_python() 426 assert ignore_case in [True, False] 427 result["IgnoreCase"] = ignore_case 428 429 # WordSearch 430 if "System`WordSearch" in keys: 431 word_search = options["System`WordSearch"].to_python() 432 assert word_search in [True, False] 433 result["WordSearch"] = word_search 434 435 # RecordSeparators 436 if "System`RecordSeparators" in keys: 437 record_separators = options["System`RecordSeparators"].to_python() 438 assert isinstance(record_separators, list) 439 assert all( 440 isinstance(s, str) and s[0] == s[-1] == '"' for s in record_separators 441 ) 442 record_separators = [s[1:-1] for s in record_separators] 443 result["RecordSeparators"] = record_separators 444 445 # WordSeparators 446 if "System`WordSeparators" in keys: 447 word_separators = options["System`WordSeparators"].to_python() 448 assert isinstance(word_separators, list) 449 assert all( 450 isinstance(s, str) and s[0] == s[-1] == '"' for s in word_separators 451 ) 452 word_separators = [s[1:-1] for s in word_separators] 453 result["WordSeparators"] = word_separators 454 455 # NullRecords 456 if "System`NullRecords" in keys: 457 null_records = options["System`NullRecords"].to_python() 458 assert null_records in [True, False] 459 result["NullRecords"] = null_records 460 461 # NullWords 462 if "System`NullWords" in keys: 463 null_words = options["System`NullWords"].to_python() 464 assert null_words in [True, False] 465 result["NullWords"] = null_words 466 467 # TokenWords 468 if "System`TokenWords" in keys: 469 token_words = options["System`TokenWords"].to_python() 470 assert token_words == [] 471 result["TokenWords"] = token_words 472 473 return result 474 475 def apply(self, channel, types, evaluation, options): 476 "Read[channel_, types_, OptionsPattern[Read]]" 477 478 if channel.has_form("OutputStream", 2): 479 evaluation.message("General", "openw", channel) 480 return 481 482 strm = _channel_to_stream(channel, "r") 483 484 if strm is None: 485 return 486 487 [name, n] = strm.get_leaves() 488 489 stream = stream_manager.lookup_stream(n.get_int_value()) 490 if stream is None: 491 evaluation.message("Read", "openx", strm) 492 return 493 if stream.io is None: 494 stream.__enter__() 495 496 if stream.io.closed: 497 evaluation.message("Read", "openx", strm) 498 return 499 500 # Wrap types in a list (if it isn't already one) 501 if types.has_form("List", None): 502 types = types._leaves 503 else: 504 types = (types,) 505 506 # TODO: look for a better implementation handling "Hold[Expression]". 507 # 508 types = ( 509 Symbol("HoldExpression") 510 if ( 511 typ.get_head_name() == "System`Hold" 512 and typ.leaves[0].get_name() == "System`Expression" 513 ) 514 else typ 515 for typ in types 516 ) 517 types = Expression("List", *types) 518 519 READ_TYPES = [ 520 Symbol(k) 521 for k in [ 522 "Byte", 523 "Character", 524 "Expression", 525 "HoldExpression", 526 "Number", 527 "Real", 528 "Record", 529 "String", 530 "Word", 531 ] 532 ] 533 534 for typ in types.leaves: 535 if typ not in READ_TYPES: 536 evaluation.message("Read", "readf", typ) 537 return SymbolFailed 538 539 # Options 540 # TODO Implement extra options 541 py_options = self.check_options(options) 542 # null_records = py_options['NullRecords'] 543 # null_words = py_options['NullWords'] 544 record_separators = py_options["RecordSeparators"] 545 # token_words = py_options['TokenWords'] 546 word_separators = py_options["WordSeparators"] 547 548 name = name.to_python() 549 550 result = [] 551 552 def reader(stream, word_separators, accepted=None): 553 while True: 554 word = "" 555 while True: 556 try: 557 tmp = stream.io.read(1) 558 except UnicodeDecodeError: 559 tmp = " " # ignore 560 evaluation.message("General", "ucdec") 561 562 if tmp == "": 563 if word == "": 564 pos = stream.io.tell() 565 newchar = stream.io.read(1) 566 if pos == stream.io.tell(): 567 raise EOFError 568 else: 569 if newchar: 570 word = newchar 571 continue 572 else: 573 yield word 574 continue 575 last_word = word 576 word = "" 577 yield last_word 578 break 579 580 if tmp in word_separators: 581 if word == "": 582 continue 583 if stream.io.seekable(): 584 # stream.io.seek(-1, 1) #Python3 585 stream.io.seek(stream.io.tell() - 1) 586 last_word = word 587 word = "" 588 yield last_word 589 break 590 591 if accepted is not None and tmp not in accepted: 592 last_word = word 593 word = "" 594 yield last_word 595 break 596 597 word += tmp 598 599 read_word = reader(stream, word_separators) 600 read_record = reader(stream, record_separators) 601 read_number = reader( 602 stream, 603 word_separators + record_separators, 604 ["+", "-", "."] + [str(i) for i in range(10)], 605 ) 606 read_real = reader( 607 stream, 608 word_separators + record_separators, 609 ["+", "-", ".", "e", "E", "^", "*"] + [str(i) for i in range(10)], 610 ) 611 for typ in types.leaves: 612 try: 613 if typ == Symbol("Byte"): 614 tmp = stream.io.read(1) 615 if tmp == "": 616 raise EOFError 617 result.append(ord(tmp)) 618 elif typ == Symbol("Character"): 619 tmp = stream.io.read(1) 620 if tmp == "": 621 raise EOFError 622 result.append(tmp) 623 elif typ == Symbol("Expression") or typ == Symbol("HoldExpression"): 624 tmp = next(read_record) 625 while True: 626 try: 627 feeder = MathicsMultiLineFeeder(tmp) 628 expr = parse(evaluation.definitions, feeder) 629 break 630 except (IncompleteSyntaxError, InvalidSyntaxError): 631 try: 632 nextline = next(read_record) 633 tmp = tmp + "\n" + nextline 634 except EOFError: 635 expr = SymbolEndOfFile 636 break 637 except Exception as e: 638 print(e) 639 640 if expr == SymbolEndOfFile: 641 evaluation.message( 642 "Read", "readt", tmp, Expression("InputSteam", name, n) 643 ) 644 return SymbolFailed 645 elif isinstance(expr, BaseExpression): 646 if typ == Symbol("HoldExpression"): 647 expr = Expression("Hold", expr) 648 result.append(expr) 649 # else: 650 # TODO: Supposedly we can't get here 651 # what code should we put here? 652 653 elif typ == Symbol("Number"): 654 tmp = next(read_number) 655 try: 656 tmp = int(tmp) 657 except ValueError: 658 try: 659 tmp = float(tmp) 660 except ValueError: 661 evaluation.message( 662 "Read", "readn", Expression("InputSteam", name, n) 663 ) 664 return SymbolFailed 665 result.append(tmp) 666 667 elif typ == Symbol("Real"): 668 tmp = next(read_real) 669 tmp = tmp.replace("*^", "E") 670 try: 671 tmp = float(tmp) 672 except ValueError: 673 evaluation.message( 674 "Read", "readn", Expression("InputSteam", name, n) 675 ) 676 return SymbolFailed 677 result.append(tmp) 678 elif typ == Symbol("Record"): 679 result.append(next(read_record)) 680 elif typ == Symbol("String"): 681 tmp = stream.io.readline() 682 if len(tmp) == 0: 683 raise EOFError 684 result.append(tmp.rstrip("\n")) 685 elif typ == Symbol("Word"): 686 result.append(next(read_word)) 687 688 except EOFError: 689 return SymbolEndOfFile 690 except UnicodeDecodeError: 691 evaluation.message("General", "ucdec") 692 693 if len(result) == 1: 694 return from_python(*result) 695 696 return from_python(result) 697 698 def apply_nostream(self, arg1, arg2, evaluation): 699 "Read[arg1_, arg2_]" 700 evaluation.message("General", "stream", arg1) 701 return 702 703 704class Write(Builtin): 705 """ 706 <dl> 707 <dt>'Write[$channel$, $expr1$, $expr2$, ...]' 708 <dd>writes the expressions to the output channel followed by a newline. 709 </dl> 710 711 >> stream = OpenWrite[] 712 = ... 713 >> Write[stream, 10 x + 15 y ^ 2] 714 >> Write[stream, 3 Sin[z]] 715 >> Close[stream] 716 = ... 717 >> stream = OpenRead[%]; 718 >> ReadList[stream] 719 = {10 x + 15 y ^ 2, 3 Sin[z]} 720 #> Close[stream]; 721 """ 722 723 attributes = "Protected" 724 725 def apply(self, channel, expr, evaluation): 726 "Write[channel_, expr___]" 727 728 strm = _channel_to_stream(channel) 729 730 if strm is None: 731 return 732 733 n = strm.leaves[1].get_int_value() 734 stream = stream_manager.lookup_stream(n) 735 736 if stream is None or stream.io is None or stream.io.closed: 737 evaluation.message("General", "openx", channel) 738 return SymbolNull 739 740 expr = expr.get_sequence() 741 expr = Expression("Row", Expression("List", *expr)) 742 743 evaluation.format = "text" 744 text = evaluation.format_output(from_python(expr)) 745 stream.io.write(str(text) + "\n") 746 return SymbolNull 747 748 749class _BinaryFormat(object): 750 """ 751 Container for BinaryRead readers and BinaryWrite writers 752 """ 753 754 @staticmethod 755 def _IEEE_real(real): 756 if math.isnan(real): 757 return Symbol("Indeterminate") 758 elif math.isinf(real): 759 return Expression("DirectedInfinity", Integer((-1) ** (real < 0))) 760 else: 761 return Real(real) 762 763 @staticmethod 764 def _IEEE_cmplx(real, imag): 765 if math.isnan(real) or math.isnan(imag): 766 return Symbol("Indeterminate") 767 elif math.isinf(real) or math.isinf(imag): 768 if math.isinf(real) and math.isinf(imag): 769 return Symbol("Indeterminate") 770 return Expression( 771 "DirectedInfinity", 772 Expression( 773 "Complex", 774 (-1) ** (real < 0) if math.isinf(real) else 0, 775 (-1) ** (imag < 0) if math.isinf(imag) else 0, 776 ), 777 ) 778 else: 779 return Complex(MachineReal(real), MachineReal(imag)) 780 781 @classmethod 782 def get_readers(cls): 783 readers = {} 784 for funcname in dir(cls): 785 if funcname.startswith("_") and funcname.endswith("_reader"): 786 readers[funcname[1:-7]] = getattr(cls, funcname) 787 return readers 788 789 @classmethod 790 def get_writers(cls): 791 writers = {} 792 for funcname in dir(cls): 793 if funcname.startswith("_") and funcname.endswith("_writer"): 794 writers[funcname[1:-7]] = getattr(cls, funcname) 795 return writers 796 797 # Reader Functions 798 799 @staticmethod 800 def _Byte_reader(s): 801 "8-bit unsigned integer" 802 return Integer(*struct.unpack("B", s.read(1))) 803 804 @staticmethod 805 def _Character8_reader(s): 806 "8-bit character" 807 return String(struct.unpack("c", s.read(1))[0].decode("ascii")) 808 809 @staticmethod 810 def _Character16_reader(s): 811 "16-bit character" 812 return String(chr(*struct.unpack("H", s.read(2)))) 813 814 @staticmethod 815 def _Complex64_reader(s): 816 "IEEE single-precision complex number" 817 return _BinaryFormat._IEEE_cmplx(*struct.unpack("ff", s.read(8))) 818 819 @staticmethod 820 def _Complex128_reader(s): 821 "IEEE double-precision complex number" 822 return _BinaryFormat._IEEE_cmplx(*struct.unpack("dd", s.read(16))) 823 824 def _Complex256_reader(self, s): 825 "IEEE quad-precision complex number" 826 return Complex(self._Real128_reader(s), self._Real128_reader(s)) 827 828 @staticmethod 829 def _Integer8_reader(s): 830 "8-bit signed integer" 831 return Integer(*struct.unpack("b", s.read(1))) 832 833 @staticmethod 834 def _Integer16_reader(s): 835 "16-bit signed integer" 836 return Integer(*struct.unpack("h", s.read(2))) 837 838 @staticmethod 839 def _Integer24_reader(s): 840 "24-bit signed integer" 841 b = s.read(3) 842 return Integer(struct.unpack("<i", b"\x00" + b)[0] >> 8) 843 844 @staticmethod 845 def _Integer32_reader(s): 846 "32-bit signed integer" 847 return Integer(*struct.unpack("i", s.read(4))) 848 849 @staticmethod 850 def _Integer64_reader(s): 851 "64-bit signed integer" 852 return Integer(*struct.unpack("q", s.read(8))) 853 854 @staticmethod 855 def _Integer128_reader(s): 856 "128-bit signed integer" 857 a, b = struct.unpack("Qq", s.read(16)) 858 return Integer((b << 64) + a) 859 860 @staticmethod 861 def _Real32_reader(s): 862 "IEEE single-precision real number" 863 return _BinaryFormat._IEEE_real(*struct.unpack("f", s.read(4))) 864 865 @staticmethod 866 def _Real64_reader(s): 867 "IEEE double-precision real number" 868 return _BinaryFormat._IEEE_real(*struct.unpack("d", s.read(8))) 869 870 @staticmethod 871 def _Real128_reader(s): 872 "IEEE quad-precision real number" 873 # Workaround quad missing from struct 874 # correctness is not guaranteed 875 b = s.read(16) 876 sig, sexp = b[:14], b[14:] 877 878 # Sign / Exponent 879 (sexp,) = struct.unpack("H", sexp) 880 signbit = sexp // 0x8000 881 expbits = sexp % 0x8000 882 883 # Signifand 884 try: 885 fracbits = int.from_bytes(sig, byteorder="little") 886 except AttributeError: # Py2 887 fracbits = int(sig[::-1].encode("hex"), 16) 888 889 if expbits == 0x0000 and fracbits == 0: 890 return Real(sympy.Float(0, 4965)) 891 elif expbits == 0x7FFF: 892 if fracbits == 0: 893 return Expression("DirectedInfinity", Integer((-1) ** signbit)) 894 else: 895 return Symbol("Indeterminate") 896 897 with mpmath.workprec(112): 898 core = mpmath.fdiv(fracbits, 2 ** 112) 899 if expbits == 0x000: 900 assert fracbits != 0 901 exp = -16382 902 core = mpmath.fmul((-1) ** signbit, core) 903 else: 904 assert 0x0001 <= expbits <= 0x7FFE 905 exp = expbits - 16383 906 core = mpmath.fmul((-1) ** signbit, mpmath.fadd(1, core)) 907 908 if exp >= 0: 909 result = mpmath.fmul(core, 2 ** exp) 910 else: 911 result = mpmath.fdiv(core, 2 ** -exp) 912 913 return from_mpmath(result, dps(112)) 914 915 @staticmethod 916 def _TerminatedString_reader(s): 917 "null-terminated string of 8-bit characters" 918 b = s.read(1) 919 contents = b"" 920 while b != b"\x00": 921 if b == b"": 922 raise struct.error 923 contents += b 924 b = s.read(1) 925 return String(contents.decode("ascii")) 926 927 @staticmethod 928 def _UnsignedInteger8_reader(s): 929 "8-bit unsigned integer" 930 return Integer(*struct.unpack("B", s.read(1))) 931 932 @staticmethod 933 def _UnsignedInteger16_reader(s): 934 "16-bit unsigned integer" 935 return Integer(*struct.unpack("H", s.read(2))) 936 937 @staticmethod 938 def _UnsignedInteger24_reader(s): 939 "24-bit unsigned integer" 940 return Integer(*struct.unpack("I", s.read(3) + b"\0")) 941 942 @staticmethod 943 def _UnsignedInteger32_reader(s): 944 "32-bit unsigned integer" 945 return Integer(*struct.unpack("I", s.read(4))) 946 947 @staticmethod 948 def _UnsignedInteger64_reader(s): 949 "64-bit unsigned integer" 950 return Integer(*struct.unpack("Q", s.read(8))) 951 952 @staticmethod 953 def _UnsignedInteger128_reader(s): 954 "128-bit unsigned integer" 955 a, b = struct.unpack("QQ", s.read(16)) 956 return Integer((b << 64) + a) 957 958 # Writer Functions 959 960 @staticmethod 961 def _Byte_writer(s, x): 962 "8-bit unsigned integer" 963 s.write(struct.pack("B", x)) 964 965 @staticmethod 966 def _Character8_writer(s, x): 967 "8-bit character" 968 s.write(struct.pack("c", x.encode("ascii"))) 969 970 # TODO 971 # @staticmethod 972 # def _Character16_writer(s, x): 973 # "16-bit character" 974 # pass 975 976 @staticmethod 977 def _Complex64_writer(s, x): 978 "IEEE single-precision complex number" 979 s.write(struct.pack("ff", x.real, x.imag)) 980 # return _BinaryFormat._IEEE_cmplx(*struct.unpack('ff', s.read(8))) 981 982 @staticmethod 983 def _Complex128_writer(s, x): 984 "IEEE double-precision complex number" 985 s.write(struct.pack("dd", x.real, x.imag)) 986 987 # TODO 988 # @staticmethod 989 # def _Complex256_writer(s, x): 990 # "IEEE quad-precision complex number" 991 # pass 992 993 @staticmethod 994 def _Integer8_writer(s, x): 995 "8-bit signed integer" 996 s.write(struct.pack("b", x)) 997 998 @staticmethod 999 def _Integer16_writer(s, x): 1000 "16-bit signed integer" 1001 s.write(struct.pack("h", x)) 1002 1003 @staticmethod 1004 def _Integer24_writer(s, x): 1005 "24-bit signed integer" 1006 s.write(struct.pack("i", x << 8)[1:]) 1007 1008 @staticmethod 1009 def _Integer32_writer(s, x): 1010 "32-bit signed integer" 1011 s.write(struct.pack("i", x)) 1012 1013 @staticmethod 1014 def _Integer64_writer(s, x): 1015 "64-bit signed integer" 1016 s.write(struct.pack("q", x)) 1017 1018 @staticmethod 1019 def _Integer128_writer(s, x): 1020 "128-bit signed integer" 1021 a, b = x & 0xFFFFFFFFFFFFFFFF, x >> 64 1022 s.write(struct.pack("Qq", a, b)) 1023 1024 @staticmethod 1025 def _Real32_writer(s, x): 1026 "IEEE single-precision real number" 1027 s.write(struct.pack("f", x)) 1028 1029 @staticmethod 1030 def _Real64_writer(s, x): 1031 "IEEE double-precision real number" 1032 s.write(struct.pack("d", x)) 1033 1034 # TODO 1035 # @staticmethod 1036 # def _Real128_writer(s, x): 1037 # "IEEE quad-precision real number" 1038 # pass 1039 1040 @staticmethod 1041 def _TerminatedString_writer(s, x): 1042 "null-terminated string of 8-bit characters" 1043 s.write(x.encode("utf-8")) 1044 1045 @staticmethod 1046 def _UnsignedInteger8_writer(s, x): 1047 "8-bit unsigned integer" 1048 s.write(struct.pack("B", x)) 1049 1050 @staticmethod 1051 def _UnsignedInteger16_writer(s, x): 1052 "16-bit unsigned integer" 1053 s.write(struct.pack("H", x)) 1054 1055 @staticmethod 1056 def _UnsignedInteger24_writer(s, x): 1057 "24-bit unsigned integer" 1058 s.write(struct.pack("I", x << 8)[1:]) 1059 1060 @staticmethod 1061 def _UnsignedInteger32_writer(s, x): 1062 "32-bit unsigned integer" 1063 s.write(struct.pack("I", x)) 1064 1065 @staticmethod 1066 def _UnsignedInteger64_writer(s, x): 1067 "64-bit unsigned integer" 1068 s.write(struct.pack("Q", x)) 1069 1070 @staticmethod 1071 def _UnsignedInteger128_writer(s, x): 1072 "128-bit unsigned integer" 1073 a, b = x & 0xFFFFFFFFFFFFFFFF, x >> 64 1074 s.write(struct.pack("QQ", a, b)) 1075 1076 1077class BinaryWrite(Builtin): 1078 """ 1079 <dl> 1080 <dt>'BinaryWrite[$channel$, $b$]' 1081 <dd>writes a single byte given as an integer from 0 to 255. 1082 <dt>'BinaryWrite[$channel$, {b1, b2, ...}]' 1083 <dd>writes a sequence of byte. 1084 <dt>'BinaryWrite[$channel$, "string"]' 1085 <dd>writes the raw characters in a string. 1086 <dt>'BinaryWrite[$channel$, $x$, $type$]' 1087 <dd>writes $x$ as the specified type. 1088 <dt>'BinaryWrite[$channel$, {$x1$, $x2$, ...}, $type$]' 1089 <dd>writes a sequence of objects as the specified type. 1090 <dt>'BinaryWrite[$channel$, {$x1$, $x2$, ...}, {$type1$, $type2$, ...}]' 1091 <dd>writes a sequence of objects using a sequence of specified types. 1092 </dl> 1093 1094 >> strm = OpenWrite[BinaryFormat -> True] 1095 = OutputStream[...] 1096 >> BinaryWrite[strm, {39, 4, 122}] 1097 = OutputStream[...] 1098 >> Close[strm] 1099 = ... 1100 >> strm = OpenRead[%, BinaryFormat -> True] 1101 = InputStream[...] 1102 >> BinaryRead[strm] 1103 = 39 1104 >> BinaryRead[strm, "Byte"] 1105 = 4 1106 >> BinaryRead[strm, "Character8"] 1107 = z 1108 >> Close[strm]; 1109 1110 Write a String 1111 >> strm = OpenWrite[BinaryFormat -> True] 1112 = OutputStream[...] 1113 >> BinaryWrite[strm, "abc123"] 1114 = OutputStream[...] 1115 >> Close[%] 1116 = ... 1117 1118 Read as Bytes 1119 >> strm = OpenRead[%, BinaryFormat -> True] 1120 = InputStream[...] 1121 >> BinaryRead[strm, {"Character8", "Character8", "Character8", "Character8", "Character8", "Character8", "Character8"}] 1122 = {a, b, c, 1, 2, 3, EndOfFile} 1123 >> Close[strm] 1124 = ... 1125 1126 Read as Characters 1127 >> strm = OpenRead[%, BinaryFormat -> True] 1128 = InputStream[...] 1129 >> BinaryRead[strm, {"Byte", "Byte", "Byte", "Byte", "Byte", "Byte", "Byte"}] 1130 = {97, 98, 99, 49, 50, 51, EndOfFile} 1131 >> Close[strm] 1132 = ... 1133 1134 Write Type 1135 >> strm = OpenWrite[BinaryFormat -> True] 1136 = OutputStream[...] 1137 >> BinaryWrite[strm, 97, "Byte"] 1138 = OutputStream[...] 1139 >> BinaryWrite[strm, {97, 98, 99}, {"Byte", "Byte", "Byte"}] 1140 = OutputStream[...] 1141 >> Close[%] 1142 = ... 1143 1144 ## Write then Read as Bytes 1145 #> WRb[bytes_, form_] := Module[{stream, res={}, byte}, stream = OpenWrite[BinaryFormat -> True]; BinaryWrite[stream, bytes, form]; stream = OpenRead[Close[stream], BinaryFormat -> True]; While[Not[SameQ[byte = BinaryRead[stream], EndOfFile]], res = Join[res, {byte}];]; Close[stream]; res] 1146 1147 ## Byte 1148 #> WRb[{149, 2, 177, 132}, {"Byte", "Byte", "Byte", "Byte"}] 1149 = {149, 2, 177, 132} 1150 #> WRb[{149, 2, 177, 132}, {"Byte", "Byte", "Byte", "Byte"}] 1151 = {149, 2, 177, 132} 1152 #> (# == WRb[#, Table["Byte", {50}]]) & [RandomInteger[{0, 255}, 50]] 1153 = True 1154 1155 ## Character8 1156 #> WRb[{"a", "b", "c"}, {"Character8", "Character8", "Character8"}] 1157 = {97, 98, 99} 1158 #> WRb[{34, 60, 39}, {"Character8", "Character8", "Character8"}] 1159 = {51, 52, 54, 48, 51, 57} 1160 #> WRb[{"ab", "c", "d"}, {"Character8", "Character8", "Character8", "Character8"}] 1161 = {97, 98, 99, 100} 1162 1163 ## Character16 1164 ## TODO 1165 1166 ## Complex64 1167 #> WRb[-6.36877988924*^28 + 3.434203392*^9 I, "Complex64"] 1168 = {80, 201, 77, 239, 201, 177, 76, 79} 1169 #> WRb[-6.98948862335*^24 + 1.52209021297*^23 I, "Complex64"] 1170 = {158, 2, 185, 232, 18, 237, 0, 102} 1171 #> WRb[-1.41079828148*^-19 - 0.013060791418 I, "Complex64"] 1172 = {195, 142, 38, 160, 238, 252, 85, 188} 1173 #> WRb[{5, -2054}, "Complex64"] 1174 = {0, 0, 160, 64, 0, 0, 0, 0, 0, 96, 0, 197, 0, 0, 0, 0} 1175 #> WRb[Infinity, "Complex64"] 1176 = {0, 0, 128, 127, 0, 0, 0, 0} 1177 #> WRb[-Infinity, "Complex64"] 1178 = {0, 0, 128, 255, 0, 0, 0, 0} 1179 #> WRb[DirectedInfinity[1 + I], "Complex64"] 1180 = {0, 0, 128, 127, 0, 0, 128, 127} 1181 #> WRb[DirectedInfinity[I], "Complex64"] 1182 = {0, 0, 0, 0, 0, 0, 128, 127} 1183 ## FIXME (different convention to MMA) 1184 #> WRb[Indeterminate, "Complex64"] 1185 = {0, 0, 192, 127, 0, 0, 192, 127} 1186 1187 ## Complex128 1188 #> WRb[1.19839770357*^-235 - 2.64656391494*^-54 I,"Complex128"] 1189 = {102, 217, 1, 163, 234, 98, 40, 15, 243, 104, 116, 15, 48, 57, 208, 180} 1190 #> WRb[3.22170267142*^134 - 8.98364297498*^198 I,"Complex128"] 1191 = {219, 161, 12, 126, 47, 94, 220, 91, 189, 66, 29, 68, 147, 11, 62, 233} 1192 #> WRb[-Infinity, "Complex128"] 1193 = {0, 0, 0, 0, 0, 0, 240, 255, 0, 0, 0, 0, 0, 0, 0, 0} 1194 #> WRb[DirectedInfinity[1 - I], "Complex128"] 1195 = {0, 0, 0, 0, 0, 0, 240, 127, 0, 0, 0, 0, 0, 0, 240, 255} 1196 #> WRb[DirectedInfinity[I], "Complex128"] 1197 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 127} 1198 ## FIXME (different convention to MMA) 1199 #> WRb[Indeterminate, "Complex128"] 1200 = {0, 0, 0, 0, 0, 0, 248, 127, 0, 0, 0, 0, 0, 0, 248, 127} 1201 1202 ## Complex256 1203 ## TODO 1204 1205 ## Integer8 1206 #> WRb[{5, 2, 11, -4}, {"Integer8", "Integer8", "Integer8", "Integer8"}] 1207 = {5, 2, 11, 252} 1208 #> WRb[{127, -128, 0}, {"Integer8", "Integer8", "Integer8"}] 1209 = {127, 128, 0} 1210 1211 ## Integer16 1212 #> WRb[{661, -31567, 6256}, {"Integer16", "Integer16", "Integer16"}] 1213 = {149, 2, 177, 132, 112, 24} 1214 #> WRb[{0, 255, -1, 32640, -32640}, Table["Integer16", {5}]] 1215 = {0, 0, 255, 0, 255, 255, 128, 127, 128, 128} 1216 1217 ## Integer24 1218 #> WRb[{-6247016, -6631492}, {"Integer24", "Integer24"}] 1219 = {152, 173, 160, 188, 207, 154} 1220 #> WRb[{-1593967, 1989169}, {"Integer24", "Integer24"}] 1221 = {145, 173, 231, 49, 90, 30} 1222 1223 ## Integer32 1224 #> WRb[{-636001327, -236143729}, {"Integer32", "Integer32"}] 1225 = {209, 99, 23, 218, 143, 187, 236, 241} 1226 #> WRb[{2024611599, -1139645195}, {"Integer32", "Integer32"}] 1227 = {15, 31, 173, 120, 245, 100, 18, 188} 1228 1229 ## Integer64 1230 #> WRb[{1176115612243989203}, "Integer64"] 1231 = {211, 18, 152, 2, 235, 102, 82, 16} 1232 #> WRb[{-8526737900550694619}, "Integer64"] 1233 = {37, 217, 208, 88, 14, 241, 170, 137} 1234 1235 ## Integer128 1236 #> WRb[139827542997232652313568968616424513676, "Integer128"] 1237 = {140, 32, 24, 199, 10, 169, 248, 117, 123, 184, 75, 76, 34, 206, 49, 105} 1238 #> WRb[103439096823027953602112616165136677221, "Integer128"] 1239 = {101, 57, 184, 108, 43, 214, 186, 120, 153, 51, 132, 225, 56, 165, 209, 77} 1240 #> WRb[-49058912464625098822365387707690163087, "Integer128"] 1241 = {113, 100, 125, 144, 211, 83, 140, 24, 206, 11, 198, 118, 222, 152, 23, 219} 1242 1243 ## Real32 1244 #> WRb[{8.398086656*^9, 1.63880017681*^16}, {"Real32", "Real32"}] 1245 = {81, 72, 250, 79, 52, 227, 104, 90} 1246 #> WRb[{5.6052915284*^32, 9.631141*^6}, {"Real32", "Real32"}] 1247 = {251, 22, 221, 117, 165, 245, 18, 75} 1248 #> WRb[Infinity, "Real32"] 1249 = {0, 0, 128, 127} 1250 #> WRb[-Infinity, "Real32"] 1251 = {0, 0, 128, 255} 1252 ## FIXME (different convention to MMA) 1253 #> WRb[Indeterminate, "Real32"] 1254 = {0, 0, 192, 127} 1255 1256 ## Real64 1257 #> WRb[-5.14646619426*^227, "Real64"] 1258 = {91, 233, 20, 87, 129, 185, 53, 239} 1259 #> WRb[-9.69531698809*^20, "Real64"] 1260 = {187, 67, 162, 67, 122, 71, 74, 196} 1261 #> WRb[9.67355569764*^159, "Real64"] 1262 = {132, 48, 80, 125, 157, 4, 38, 97} 1263 #> WRb[Infinity, "Real64"] 1264 = {0, 0, 0, 0, 0, 0, 240, 127} 1265 #> WRb[-Infinity, "Real64"] 1266 = {0, 0, 0, 0, 0, 0, 240, 255} 1267 ## FIXME (different convention to MMA) 1268 #> WRb[Indeterminate, "Real64"] 1269 = {0, 0, 0, 0, 0, 0, 248, 127} 1270 1271 ## Real128 1272 ## TODO 1273 1274 ## TerminatedString 1275 #> WRb["abc", "TerminatedString"] 1276 = {97, 98, 99, 0} 1277 #> WRb[{"123", "456"}, {"TerminatedString", "TerminatedString", "TerminatedString"}] 1278 = {49, 50, 51, 0, 52, 53, 54, 0} 1279 #> WRb["", "TerminatedString"] 1280 = {0} 1281 1282 ## UnsignedInteger8 1283 #> WRb[{96, 94, 141, 162, 141}, Table["UnsignedInteger8", {5}]] 1284 = {96, 94, 141, 162, 141} 1285 #> (#==WRb[#,Table["UnsignedInteger8",{50}]])&[RandomInteger[{0, 255}, 50]] 1286 = True 1287 1288 ## UnsignedInteger16 1289 #> WRb[{18230, 47466, 9875, 59141}, Table["UnsignedInteger16", {4}]] 1290 = {54, 71, 106, 185, 147, 38, 5, 231} 1291 #> WRb[{0, 32896, 65535}, Table["UnsignedInteger16", {3}]] 1292 = {0, 0, 128, 128, 255, 255} 1293 1294 ## UnsignedInteger24 1295 #> WRb[{14820174, 15488225}, Table["UnsignedInteger24", {2}]] 1296 = {78, 35, 226, 225, 84, 236} 1297 #> WRb[{5374629, 3889391}, Table["UnsignedInteger24", {2}]] 1298 = {165, 2, 82, 239, 88, 59} 1299 1300 ## UnsignedInteger32 1301 #> WRb[{1885507541, 4157323149}, Table["UnsignedInteger32", {2}]] 1302 = {213, 143, 98, 112, 141, 183, 203, 247} 1303 #> WRb[{384206740, 1676316040}, Table["UnsignedInteger32", {2}]] 1304 = {148, 135, 230, 22, 136, 141, 234, 99} 1305 """ 1306 1307 messages = { 1308 "writex": "`1`.", 1309 } 1310 1311 writers = _BinaryFormat.get_writers() 1312 1313 def apply_notype(self, name, n, b, evaluation): 1314 "BinaryWrite[OutputStream[name_, n_], b_]" 1315 return self.apply(name, n, b, None, evaluation) 1316 1317 def apply(self, name, n, b, typ, evaluation): 1318 "BinaryWrite[OutputStream[name_, n_], b_, typ_]" 1319 1320 channel = Expression("OutputStream", name, n) 1321 1322 # Check Empty Type 1323 if typ is None: 1324 expr = Expression("BinaryWrite", channel, b) 1325 typ = Expression("List") 1326 else: 1327 expr = Expression("BinaryWrite", channel, b, typ) 1328 1329 # Check channel 1330 stream = stream_manager.lookup_stream(n.get_int_value()) 1331 1332 if stream is None or stream.io.closed: 1333 evaluation.message("General", "openx", name) 1334 return expr 1335 1336 if stream.mode not in ["wb", "ab"]: 1337 evaluation.message("BinaryWrite", "openr", channel) 1338 return expr 1339 1340 # Check b 1341 if b.has_form("List", None): 1342 pyb = b.leaves 1343 else: 1344 pyb = [b] 1345 1346 # Check Type 1347 if typ.has_form("List", None): 1348 types = typ.get_leaves() 1349 else: 1350 types = [typ] 1351 1352 if len(types) == 0: # Default type is "Bytes" 1353 types = [String("Byte")] 1354 1355 types = [t.get_string_value() for t in types] 1356 if not all(t in self.writers for t in types): 1357 evaluation.message("BinaryRead", "format", typ) 1358 return expr 1359 1360 # Write to stream 1361 i = 0 1362 while i < len(pyb): 1363 x = pyb[i] 1364 # Types are "repeated as many times as necessary" 1365 t = types[i % len(types)] 1366 1367 # Coerce x 1368 if t == "TerminatedString": 1369 x = x.get_string_value() + "\x00" 1370 elif t.startswith("Real"): 1371 if isinstance(x, Real): 1372 x = x.to_python() 1373 elif x.has_form("DirectedInfinity", 1): 1374 if x.leaves[0].get_int_value() == 1: 1375 x = float("+inf") 1376 elif x.leaves[0].get_int_value() == -1: 1377 x = float("-inf") 1378 else: 1379 x = None 1380 elif isinstance(x, Symbol) and x.get_name() == "System`Indeterminate": 1381 x = float("nan") 1382 else: 1383 x = None 1384 assert x is None or isinstance(x, float) 1385 elif t.startswith("Complex"): 1386 if isinstance(x, (Complex, Real, Integer)): 1387 x = x.to_python() 1388 elif x.has_form("DirectedInfinity", 1): 1389 x = x.leaves[0].to_python(n_evaluation=evaluation) 1390 1391 # x*float('+inf') creates nan if x.real or x.imag are zero 1392 x = complex( 1393 x.real * float("+inf") if x.real != 0 else 0, 1394 x.imag * float("+inf") if x.imag != 0 else 0, 1395 ) 1396 elif isinstance(x, Symbol) and x.get_name() == "System`Indeterminate": 1397 x = complex(float("nan"), float("nan")) 1398 else: 1399 x = None 1400 elif t.startswith("Character"): 1401 if isinstance(x, Integer): 1402 x = [String(char) for char in str(x.get_int_value())] 1403 pyb = list(chain(pyb[:i], x, pyb[i + 1 :])) 1404 x = pyb[i] 1405 if isinstance(x, String) and len(x.get_string_value()) > 1: 1406 x = [String(char) for char in x.get_string_value()] 1407 pyb = list(chain(pyb[:i], x, pyb[i + 1 :])) 1408 x = pyb[i] 1409 x = x.get_string_value() 1410 elif t == "Byte" and isinstance(x, String): 1411 if len(x.get_string_value()) > 1: 1412 x = [String(char) for char in x.get_string_value()] 1413 pyb = list(chain(pyb[:i], x, pyb[i + 1 :])) 1414 x = pyb[i] 1415 x = ord(x.get_string_value()) 1416 else: 1417 x = x.get_int_value() 1418 1419 if x is None: 1420 return evaluation.message("BinaryWrite", "nocoerce", b) 1421 1422 try: 1423 self.writers[t](stream.io, x) 1424 except struct.error: 1425 return evaluation.message("BinaryWrite", "nocoerce", b) 1426 i += 1 1427 1428 try: 1429 stream.io.flush() 1430 except IOError as err: 1431 evaluation.message("BinaryWrite", "writex", err.strerror) 1432 return channel 1433 1434 1435class BinaryRead(Builtin): 1436 """ 1437 <dl> 1438 <dt>'BinaryRead[$stream$]' 1439 <dd>reads one byte from the stream as an integer from 0 to 255. 1440 <dt>'BinaryRead[$stream$, $type$]' 1441 <dd>reads one object of specified type from the stream. 1442 <dt>'BinaryRead[$stream$, {$type1$, $type2$, ...}]' 1443 <dd>reads a sequence of objects of specified types. 1444 </dl> 1445 1446 >> strm = OpenWrite[BinaryFormat -> True] 1447 = OutputStream[...] 1448 >> BinaryWrite[strm, {97, 98, 99}] 1449 = OutputStream[...] 1450 >> Close[strm] 1451 = ... 1452 >> strm = OpenRead[%, BinaryFormat -> True] 1453 = InputStream[...] 1454 >> BinaryRead[strm, {"Character8", "Character8", "Character8"}] 1455 = {a, b, c} 1456 >> Close[strm]; 1457 1458 ## Write as Bytes then Read 1459 #> WbR[bytes_, form_] := Module[{stream, res}, stream = OpenWrite[BinaryFormat -> True]; BinaryWrite[stream, bytes]; stream = OpenRead[Close[stream], BinaryFormat -> True]; res = BinaryRead[stream, form]; Close[stream]; res] 1460 1461 ## Byte 1462 #> WbR[{149, 2, 177, 132}, {"Byte", "Byte", "Byte", "Byte"}] 1463 = {149, 2, 177, 132} 1464 #> (# == WbR[#, Table["Byte", {50}]]) & [RandomInteger[{0, 255}, 50]] 1465 = True 1466 1467 ## Character8 1468 #> WbR[{97, 98, 99}, {"Character8", "Character8", "Character8"}] 1469 = {a, b, c} 1470 #> WbR[{34, 60, 39}, {"Character8", "Character8", "Character8"}] 1471 = {", <, '} 1472 1473 ## Character16 1474 #> WbR[{97, 0, 98, 0, 99, 0}, {"Character16", "Character16", "Character16"}] 1475 = {a, b, c} 1476 #> ToCharacterCode[WbR[{50, 154, 182, 236}, {"Character16", "Character16"}]] 1477 = {{39474}, {60598}} 1478 ## #> WbR[ {91, 146, 206, 54}, {"Character16", "Character16"}] 1479 ## = {\\:925b, \\:36ce} 1480 1481 ## Complex64 1482 #> WbR[{80, 201, 77, 239, 201, 177, 76, 79}, "Complex64"] // InputForm 1483 = -6.368779889243691*^28 + 3.434203392*^9*I 1484 #> % // Precision 1485 = MachinePrecision 1486 #> WbR[{158, 2, 185, 232, 18, 237, 0, 102}, "Complex64"] // InputForm 1487 = -6.989488623351118*^24 + 1.522090212973691*^23*I 1488 #> WbR[{195, 142, 38, 160, 238, 252, 85, 188}, "Complex64"] // InputForm 1489 = -1.4107982814807285*^-19 - 0.013060791417956352*I 1490 1491 ## Complex128 1492 #> WbR[{15,114,1,163,234,98,40,15,214,127,116,15,48,57,208,180},"Complex128"] // InputForm 1493 = 1.1983977035653814*^-235 - 2.6465639149433955*^-54*I 1494 #> WbR[{148,119,12,126,47,94,220,91,42,69,29,68,147,11,62,233},"Complex128"] // InputForm 1495 = 3.2217026714156333*^134 - 8.98364297498066*^198*I 1496 #> % // Precision 1497 = MachinePrecision 1498 #> WbR[{15,42,80,125,157,4,38,97, 0,0,0,0,0,0,240,255}, "Complex128"] 1499 = -I Infinity 1500 #> WbR[{15,42,80,125,157,4,38,97, 0,0,0,0,0,0,240,127}, "Complex128"] 1501 = I Infinity 1502 #> WbR[{15,42,80,125,157,4,38,97, 1,0,0,0,0,0,240,255}, "Complex128"] 1503 = Indeterminate 1504 #> WbR[{0,0,0,0,0,0,240,127, 15,42,80,125,157,4,38,97}, "Complex128"] 1505 = Infinity 1506 #> WbR[{0,0,0,0,0,0,240,255, 15,42,80,125,157,4,38,97}, "Complex128"] 1507 = -Infinity 1508 #> WbR[{1,0,0,0,0,0,240,255, 15,42,80,125,157,4,38,97}, "Complex128"] 1509 = Indeterminate 1510 #> WbR[{0,0,0,0,0,0,240,127, 0,0,0,0,0,0,240,127}, "Complex128"] 1511 = Indeterminate 1512 #> WbR[{0,0,0,0,0,0,240,127, 0,0,0,0,0,0,240,255}, "Complex128"] 1513 = Indeterminate 1514 1515 ## Complex256 1516 ## TODO 1517 1518 ## Integer8 1519 #> WbR[{149, 2, 177, 132}, {"Integer8", "Integer8", "Integer8", "Integer8"}] 1520 = {-107, 2, -79, -124} 1521 #> WbR[{127, 128, 0, 255}, {"Integer8", "Integer8", "Integer8", "Integer8"}] 1522 = {127, -128, 0, -1} 1523 1524 ## Integer16 1525 #> WbR[{149, 2, 177, 132, 112, 24}, {"Integer16", "Integer16", "Integer16"}] 1526 = {661, -31567, 6256} 1527 #> WbR[{0, 0, 255, 0, 255, 255, 128, 127, 128, 128}, Table["Integer16", {5}]] 1528 = {0, 255, -1, 32640, -32640} 1529 1530 ## Integer24 1531 #> WbR[{152, 173, 160, 188, 207, 154}, {"Integer24", "Integer24"}] 1532 = {-6247016, -6631492} 1533 #> WbR[{145, 173, 231, 49, 90, 30}, {"Integer24", "Integer24"}] 1534 = {-1593967, 1989169} 1535 1536 ## Integer32 1537 #> WbR[{209, 99, 23, 218, 143, 187, 236, 241}, {"Integer32", "Integer32"}] 1538 = {-636001327, -236143729} 1539 #> WbR[{15, 31, 173, 120, 245, 100, 18, 188}, {"Integer32", "Integer32"}] 1540 = {2024611599, -1139645195} 1541 1542 ## Integer64 1543 #> WbR[{211, 18, 152, 2, 235, 102, 82, 16}, "Integer64"] 1544 = 1176115612243989203 1545 #> WbR[{37, 217, 208, 88, 14, 241, 170, 137}, "Integer64"] 1546 = -8526737900550694619 1547 1548 ## Integer128 1549 #> WbR[{140,32,24,199,10,169,248,117,123,184,75,76,34,206,49,105}, "Integer128"] 1550 = 139827542997232652313568968616424513676 1551 #> WbR[{101,57,184,108,43,214,186,120,153,51,132,225,56,165,209,77}, "Integer128"] 1552 = 103439096823027953602112616165136677221 1553 #> WbR[{113,100,125,144,211,83,140,24,206,11,198,118,222,152,23,219}, "Integer128"] 1554 = -49058912464625098822365387707690163087 1555 1556 ## Real32 1557 #> WbR[{81, 72, 250, 79, 52, 227, 104, 90}, {"Real32", "Real32"}] // InputForm 1558 = {8.398086656*^9, 1.6388001768669184*^16} 1559 #> WbR[{251, 22, 221, 117, 165, 245, 18, 75}, {"Real32", "Real32"}] // InputForm 1560 = {5.605291528399748*^32, 9.631141*^6} 1561 #> WbR[{126, 82, 143, 43}, "Real32"] // InputForm 1562 = 1.0183657302847982*^-12 1563 #> % // Precision 1564 = MachinePrecision 1565 #> WbR[{0, 0, 128, 127}, "Real32"] 1566 = Infinity 1567 #> WbR[{0, 0, 128, 255}, "Real32"] 1568 = -Infinity 1569 #> WbR[{1, 0, 128, 255}, "Real32"] 1570 = Indeterminate 1571 #> WbR[{1, 0, 128, 127}, "Real32"] 1572 = Indeterminate 1573 1574 ## Real64 1575 #> WbR[{45, 243, 20, 87, 129, 185, 53, 239}, "Real64"] // InputForm 1576 = -5.146466194262116*^227 1577 #> WbR[{192, 60, 162, 67, 122, 71, 74, 196}, "Real64"] // InputForm 1578 = -9.695316988087658*^20 1579 #> WbR[{15, 42, 80, 125, 157, 4, 38, 97}, "Real64"] // InputForm 1580 = 9.67355569763742*^159 1581 #> % // Precision 1582 = MachinePrecision 1583 #> WbR[{0, 0, 0, 0, 0, 0, 240, 127}, "Real64"] 1584 = Infinity 1585 #> WbR[{0, 0, 0, 0, 0, 0, 240, 255}, "Real64"] 1586 = -Infinity 1587 #> WbR[{1, 0, 0, 0, 0, 0, 240, 127}, "Real64"] 1588 = Indeterminate 1589 #> WbR[{1, 0, 0, 0, 0, 0, 240, 255}, "Real64"] 1590 = Indeterminate 1591 1592 ## Real128 1593 ## 0x0000 1594 #> WbR[{0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}, "Real128"] 1595 = 0.*^-4965 1596 #> WbR[{0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,128}, "Real128"] 1597 = 0.*^-4965 1598 ## 0x0001 - 0x7FFE 1599 #> WbR[{0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,255,63}, "Real128"] 1600 = 1.00000000000000000000000000000000 1601 #> WbR[{0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,255,191}, "Real128"] 1602 = -1.00000000000000000000000000000000 1603 #> WbR[{135, 62, 233, 137, 22, 208, 233, 210, 133, 82, 251, 92, 220, 216, 255, 63}, "Real128"] 1604 = 1.84711247573661489653389674493896 1605 #> WbR[{135, 62, 233, 137, 22, 208, 233, 210, 133, 82, 251, 92, 220, 216, 207, 72}, "Real128"] 1606 = 2.45563355727491021879689747166252*^679 1607 #> WbR[{74, 95, 30, 234, 116, 130, 1, 84, 20, 133, 245, 221, 113, 110, 219, 212}, "Real128"] 1608 = -4.52840681592341879518366539335138*^1607 1609 #> % // Precision 1610 = 33. 1611 ## 0x7FFF 1612 #> WbR[{0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,255,127}, "Real128"] 1613 = Infinity 1614 #> WbR[{0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,255,255}, "Real128"] 1615 = -Infinity 1616 #> WbR[{1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,255,127}, "Real128"] 1617 = Indeterminate 1618 #> WbR[{1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,255,255}, "Real128"] 1619 = Indeterminate 1620 1621 ## TerminatedString 1622 #> WbR[{97, 98, 99, 0}, "TerminatedString"] 1623 = abc 1624 #> WbR[{49, 50, 51, 0, 52, 53, 54, 0, 55, 56, 57}, Table["TerminatedString", {3}]] 1625 = {123, 456, EndOfFile} 1626 #> WbR[{0}, "TerminatedString"] // InputForm 1627 = "" 1628 1629 ## UnsignedInteger8 1630 #> WbR[{96, 94, 141, 162, 141}, Table["UnsignedInteger8", {5}]] 1631 = {96, 94, 141, 162, 141} 1632 #> (#==WbR[#,Table["UnsignedInteger8",{50}]])&[RandomInteger[{0, 255}, 50]] 1633 = True 1634 1635 ## UnsignedInteger16 1636 #> WbR[{54, 71, 106, 185, 147, 38, 5, 231}, Table["UnsignedInteger16", {4}]] 1637 = {18230, 47466, 9875, 59141} 1638 #> WbR[{0, 0, 128, 128, 255, 255}, Table["UnsignedInteger16", {3}]] 1639 = {0, 32896, 65535} 1640 1641 ## UnsignedInteger24 1642 #> WbR[{78, 35, 226, 225, 84, 236}, Table["UnsignedInteger24", {2}]] 1643 = {14820174, 15488225} 1644 #> WbR[{165, 2, 82, 239, 88, 59}, Table["UnsignedInteger24", {2}]] 1645 = {5374629, 3889391} 1646 1647 ## UnsignedInteger32 1648 #> WbR[{213,143,98,112,141,183,203,247}, Table["UnsignedInteger32", {2}]] 1649 = {1885507541, 4157323149} 1650 #> WbR[{148,135,230,22,136,141,234,99}, Table["UnsignedInteger32", {2}]] 1651 = {384206740, 1676316040} 1652 1653 ## UnsignedInteger64 1654 #> WbR[{95, 5, 33, 229, 29, 62, 63, 98}, "UnsignedInteger64"] 1655 = 7079445437368829279 1656 #> WbR[{134, 9, 161, 91, 93, 195, 173, 74}, "UnsignedInteger64"] 1657 = 5381171935514265990 1658 1659 ## UnsignedInteger128 1660 #> WbR[{108,78,217,150,88,126,152,101,231,134,176,140,118,81,183,220}, "UnsignedInteger128"] 1661 = 293382001665435747348222619884289871468 1662 #> WbR[{53,83,116,79,81,100,60,126,202,52,241,48,5,113,92,190}, "UnsignedInteger128"] 1663 = 253033302833692126095975097811212718901 1664 1665 ## EndOfFile 1666 #> WbR[{148}, {"Integer32", "Integer32","Integer32"}] 1667 = {EndOfFile, EndOfFile, EndOfFile} 1668 """ 1669 1670 readers = _BinaryFormat.get_readers() 1671 1672 messages = { 1673 "format": "`1` is not a recognized binary format.", 1674 "openw": "`1` is open for output.", 1675 "bfmt": "The stream `1` has been opened with BinaryFormat -> False and cannot be used with binary data.", 1676 } 1677 1678 def apply_empty(self, name, n, evaluation): 1679 "BinaryRead[InputStream[name_, n_]]" 1680 return self.apply(name, n, None, evaluation) 1681 1682 def apply(self, name, n, typ, evaluation): 1683 "BinaryRead[InputStream[name_, n_], typ_]" 1684 1685 channel = Expression("InputStream", name, n) 1686 1687 # Check typ 1688 if typ is None: 1689 expr = Expression("BinaryRead", channel) 1690 typ = String("Byte") 1691 else: 1692 expr = Expression("BinaryRead", channel, typ) 1693 1694 # Check channel 1695 stream = stream_manager.lookup_stream(n.get_int_value()) 1696 1697 if stream is None or stream.io.closed: 1698 evaluation.message("General", "openx", name) 1699 return expr 1700 1701 if stream.mode not in ["rb"]: 1702 evaluation.message("BinaryRead", "bfmt", channel) 1703 return expr 1704 1705 if typ.has_form("List", None): 1706 types = typ.get_leaves() 1707 else: 1708 types = [typ] 1709 1710 types = [t.get_string_value() for t in types] 1711 if not all(t in self.readers for t in types): 1712 evaluation.message("BinaryRead", "format", typ) 1713 return expr 1714 1715 # Read from stream 1716 result = [] 1717 for t in types: 1718 try: 1719 result.append(self.readers[t](stream.io)) 1720 except struct.error: 1721 result.append(SymbolEndOfFile) 1722 1723 if typ.has_form("List", None): 1724 return Expression("List", *result) 1725 else: 1726 if len(result) == 1: 1727 return result[0] 1728 1729 1730class WriteString(Builtin): 1731 """ 1732 <dl> 1733 <dt>'WriteString[$stream$, $str1, $str2$, ... ]' 1734 <dd>writes the strings to the output stream. 1735 </dl> 1736 1737 >> stream = OpenWrite[]; 1738 >> WriteString[stream, "This is a test 1"] 1739 >> WriteString[stream, "This is also a test 2"] 1740 >> Close[stream] 1741 = ... 1742 >> FilePrint[%] 1743 | This is a test 1This is also a test 2 1744 1745 >> stream = OpenWrite[]; 1746 >> WriteString[stream, "This is a test 1", "This is also a test 2"] 1747 >> Close[stream] 1748 = ... 1749 >> FilePrint[%] 1750 | This is a test 1This is also a test 2 1751 1752 #> stream = OpenWrite[]; 1753 #> WriteString[stream, 100, 1 + x + y, Sin[x + y]] 1754 #> Close[stream] 1755 = ... 1756 #> FilePrint[%] 1757 | 1001 + x + ySin[x + y] 1758 1759 #> stream = OpenWrite[]; 1760 #> WriteString[stream] 1761 #> Close[stream] 1762 = ... 1763 #> FilePrint[%] 1764 1765 #> WriteString[%%, abc] 1766 #> Streams[%%%][[1]] 1767 = ... 1768 #> Close[%] 1769 = ... 1770 #> FilePrint[%] 1771 | abc 1772 1773 """ 1774 1775 messages = { 1776 "strml": ("`1` is not a string, stream, " "or list of strings and streams."), 1777 "writex": "`1`.", 1778 } 1779 1780 attributes = "Protected" 1781 1782 def apply(self, channel, expr, evaluation): 1783 "WriteString[channel_, expr___]" 1784 strm = _channel_to_stream(channel, "w") 1785 1786 if strm is None: 1787 return 1788 1789 stream = stream_manager.lookup_stream(strm.leaves[1].get_int_value()) 1790 1791 if stream is None or stream.io is None or stream.io.closed: 1792 return None 1793 1794 exprs = [] 1795 for expri in expr.get_sequence(): 1796 result = expri.format(evaluation, "System`OutputForm") 1797 try: 1798 result = result.boxes_to_text(evaluation=evaluation) 1799 except BoxError: 1800 return evaluation.message( 1801 "General", 1802 "notboxes", 1803 Expression("FullForm", result).evaluate(evaluation), 1804 ) 1805 exprs.append(result) 1806 line = "".join(exprs) 1807 if type(stream) is BytesIO: 1808 line = line.encode("utf8") 1809 stream.io.write(line) 1810 try: 1811 stream.io.flush() 1812 except IOError as err: 1813 evaluation.message("WriteString", "writex", err.strerror) 1814 return SymbolNull 1815 1816 1817class _OpenAction(Builtin): 1818 1819 attributes = "Protected" 1820 1821 # BinaryFormat: 'False', 1822 # CharacterEncoding :> Automatic, 1823 # DOSTextFormat :> True, 1824 # FormatType -> InputForm, 1825 # NumberMarks :> $NumberMarks, 1826 # PageHeight -> 22, PageWidth -> 78, 1827 # TotalHeight -> Infinity, 1828 # TotalWidth -> Infinity 1829 1830 options = { 1831 "BinaryFormat": "False", 1832 "CharacterEncoding": "$CharacterEncoding", 1833 } 1834 1835 messages = { 1836 "argx": "OpenRead called with 0 arguments; 1 argument is expected.", 1837 "fstr": ( 1838 "File specification `1` is not a string of " "one or more characters." 1839 ), 1840 } 1841 1842 def apply_empty(self, evaluation, options): 1843 "%(name)s[OptionsPattern[]]" 1844 1845 if isinstance(self, (OpenWrite, OpenAppend)): 1846 tmpf = tempfile.NamedTemporaryFile(dir=TMP_DIR) 1847 path = String(tmpf.name) 1848 tmpf.close() 1849 return self.apply_path(path, evaluation, options) 1850 else: 1851 evaluation.message("OpenRead", "argx") 1852 return 1853 1854 def apply_path(self, path, evaluation, options): 1855 "%(name)s[path_?NotOptionQ, OptionsPattern[]]" 1856 1857 # Options 1858 # BinaryFormat 1859 mode = self.mode 1860 if options["System`BinaryFormat"].is_true(): 1861 if not self.mode.endswith("b"): 1862 mode += "b" 1863 1864 if not (isinstance(path, String) and len(path.to_python()) > 2): 1865 evaluation.message(self.__class__.__name__, "fstr", path) 1866 return 1867 1868 path_string = path.get_string_value() 1869 1870 tmp = path_search(path_string) 1871 if tmp is None: 1872 if mode in ["r", "rb"]: 1873 evaluation.message("General", "noopen", path) 1874 return 1875 else: 1876 path_string = tmp 1877 1878 try: 1879 encoding = self.get_option(options, "CharacterEncoding", evaluation) 1880 if not isinstance(encoding, String): 1881 return 1882 1883 opener = mathics_open( 1884 path_string, mode=mode, encoding=encoding.get_string_value() 1885 ) 1886 opener.__enter__() 1887 n = opener.n 1888 except IOError: 1889 evaluation.message("General", "noopen", path) 1890 return 1891 except MessageException as e: 1892 e.message(evaluation) 1893 return 1894 1895 return Expression(self.stream_type, path, Integer(n)) 1896 1897 1898class OpenRead(_OpenAction): 1899 """ 1900 <dl> 1901 <dt>'OpenRead["file"]' 1902 <dd>opens a file and returns an InputStream. 1903 </dl> 1904 1905 >> OpenRead["ExampleData/EinsteinSzilLetter.txt"] 1906 = InputStream[...] 1907 #> Close[%]; 1908 1909 S> OpenRead["https://raw.githubusercontent.com/mathics/Mathics/master/README.rst"] 1910 = InputStream[...] 1911 S> Close[%]; 1912 1913 #> OpenRead[] 1914 : OpenRead called with 0 arguments; 1 argument is expected. 1915 = OpenRead[] 1916 1917 #> OpenRead[y] 1918 : File specification y is not a string of one or more characters. 1919 = OpenRead[y] 1920 1921 #> OpenRead[""] 1922 : File specification is not a string of one or more characters. 1923 = OpenRead[] 1924 1925 #> OpenRead["MathicsNonExampleFile"] 1926 : Cannot open MathicsNonExampleFile. 1927 = OpenRead[MathicsNonExampleFile] 1928 1929 #> OpenRead["ExampleData/EinsteinSzilLetter.txt", BinaryFormat -> True] 1930 = InputStream[...] 1931 #> Close[%]; 1932 """ 1933 1934 mode = "r" 1935 stream_type = "InputStream" 1936 1937 1938class OpenWrite(_OpenAction): 1939 """ 1940 <dl> 1941 <dt>'OpenWrite["file"]' 1942 <dd>opens a file and returns an OutputStream. 1943 </dl> 1944 1945 >> OpenWrite[] 1946 = OutputStream[...] 1947 #> Close[%]; 1948 1949 #> OpenWrite[BinaryFormat -> True] 1950 = OutputStream[...] 1951 #> Close[%]; 1952 """ 1953 1954 mode = "w" 1955 stream_type = "OutputStream" 1956 1957 1958class OpenAppend(_OpenAction): 1959 """ 1960 <dl> 1961 <dt>'OpenAppend["file"]' 1962 <dd>opens a file and returns an OutputStream to which writes are appended. 1963 </dl> 1964 1965 >> OpenAppend[] 1966 = OutputStream[...] 1967 #> Close[%]; 1968 1969 #> appendFile = OpenAppend["MathicsNonExampleFile"] 1970 = OutputStream[MathicsNonExampleFile, ...] 1971 1972 #> Close[appendFile] 1973 = MathicsNonExampleFile 1974 #> DeleteFile["MathicsNonExampleFile"] 1975 """ 1976 1977 mode = "a" 1978 stream_type = "OutputStream" 1979 1980 1981class Get(PrefixOperator): 1982 r""" 1983 <dl> 1984 <dt>'<<$name$' 1985 <dd>reads a file and evaluates each expression, returning only the last one. 1986 1987 <dt>'Get[$name$, Trace->True]' 1988 <dd>Runs Get tracing each line before it is evaluated. 1989 </dl> 1990 1991 S> filename = $TemporaryDirectory <> "/example_file"; 1992 S> Put[x + y, filename] 1993 S> Get[filename] 1994 = x + y 1995 1996 S> filename = $TemporaryDirectory <> "/example_file"; 1997 S> Put[x + y, 2x^2 + 4z!, Cos[x] + I Sin[x], filename] 1998 S> Get[filename] 1999 = Cos[x] + I Sin[x] 2000 S> DeleteFile[filename] 2001 2002 ## TODO: Requires EndPackage implemented 2003 ## 'Get' can also load packages: 2004 ## >> << "VectorAnalysis`" 2005 2006 #> Get["SomeTypoPackage`"] 2007 : Cannot open SomeTypoPackage`. 2008 = $Failed 2009 2010 ## Parser Tests 2011 #> Hold[<< ~/some_example/dir/] // FullForm 2012 = Hold[Get["~/some_example/dir/"]] 2013 #> Hold[<<`/.\-_:$*~?] // FullForm 2014 = Hold[Get["`/.\\\\-_:$*~?"]] 2015 """ 2016 2017 operator = "<<" 2018 precedence = 720 2019 attributes = "Protected" 2020 2021 options = { 2022 "Trace": "False", 2023 } 2024 2025 def apply(self, path, evaluation, options): 2026 "Get[path_String, OptionsPattern[Get]]" 2027 2028 def check_options(options): 2029 # Options 2030 # TODO Proper error messages 2031 2032 result = {} 2033 trace_get = evaluation.parse("Settings`$TraceGet") 2034 if ( 2035 options["System`Trace"].to_python() 2036 or trace_get.evaluate(evaluation) == SymbolTrue 2037 ): 2038 import builtins 2039 2040 result["TraceFn"] = builtins.print 2041 else: 2042 result["TraceFn"] = None 2043 2044 return result 2045 2046 py_options = check_options(options) 2047 trace_fn = py_options["TraceFn"] 2048 result = None 2049 pypath = path.get_string_value() 2050 definitions = evaluation.definitions 2051 mathics.core.streams.PATH_VAR = SymbolPath.evaluate(evaluation).to_python(string_quotes=False) 2052 try: 2053 if trace_fn: 2054 trace_fn(pypath) 2055 with mathics_open(pypath, "r") as f: 2056 feeder = MathicsFileLineFeeder(f, trace_fn) 2057 while not feeder.empty(): 2058 try: 2059 query = parse(definitions, feeder) 2060 except TranslateError: 2061 return SymbolNull 2062 finally: 2063 feeder.send_messages(evaluation) 2064 if query is None: # blank line / comment 2065 continue 2066 result = query.evaluate(evaluation) 2067 except IOError: 2068 evaluation.message("General", "noopen", path) 2069 return SymbolFailed 2070 except MessageException as e: 2071 e.message(evaluation) 2072 return SymbolFailed 2073 return result 2074 2075 def apply_default(self, filename, evaluation): 2076 "Get[filename_]" 2077 expr = Expression("Get", filename) 2078 evaluation.message("General", "stream", filename) 2079 return expr 2080 2081 2082class Put(BinaryOperator): 2083 """ 2084 <dl> 2085 <dt>'$expr$ >> $filename$' 2086 <dd>write $expr$ to a file. 2087 <dt>'Put[$expr1$, $expr2$, ..., $filename$]' 2088 <dd>write a sequence of expressions to a file. 2089 </dl> 2090 2091 ## Note a lot of these tests are: 2092 ## * a bit fragile, somewhat 2093 ## * somewhat OS dependent, 2094 ## * can leave crap in the filesystem 2095 ## * put in a pytest 2096 ## 2097 ## For these reasons this should be done a a pure test 2098 ## rather than intermingled with the doc system. 2099 2100 S> Put[40!, fortyfactorial] 2101 : fortyfactorial is not string, InputStream[], or OutputStream[] 2102 = 815915283247897734345611269596115894272000000000 >> fortyfactorial 2103 ## FIXME: final line should be 2104 ## = Put[815915283247897734345611269596115894272000000000, fortyfactorial] 2105 2106 S> filename = $TemporaryDirectory <> "/fortyfactorial"; 2107 S> Put[40!, filename] 2108 S> FilePrint[filename] 2109 | 815915283247897734345611269596115894272000000000 2110 S> Get[filename] 2111 = 815915283247897734345611269596115894272000000000 2112 S> DeleteFile[filename] 2113 2114 S> filename = $TemporaryDirectory <> "/fiftyfactorial"; 2115 S> Put[10!, 20!, 30!, filename] 2116 S> FilePrint[filename] 2117 | 3628800 2118 | 2432902008176640000 2119 | 265252859812191058636308480000000 2120 2121 S> DeleteFile[filename] 2122 = 2123 2124 S> filename = $TemporaryDirectory <> "/example_file"; 2125 S> Put[x + y, 2x^2 + 4z!, Cos[x] + I Sin[x], filename] 2126 S> FilePrint[filename] 2127 | x + y 2128 | 2*x^2 + 4*z! 2129 | Cos[x] + I*Sin[x] 2130 S> DeleteFile[filename] 2131 """ 2132 2133 operator = ">>" 2134 precedence = 30 2135 2136 def apply(self, exprs, filename, evaluation): 2137 "Put[exprs___, filename_String]" 2138 instream = Expression("OpenWrite", filename).evaluate(evaluation) 2139 if len(instream.leaves) == 2: 2140 name, n = instream.leaves 2141 else: 2142 return # opening failed 2143 result = self.apply_input(exprs, name, n, evaluation) 2144 Expression("Close", instream).evaluate(evaluation) 2145 return result 2146 2147 def apply_input(self, exprs, name, n, evaluation): 2148 "Put[exprs___, OutputStream[name_, n_]]" 2149 stream = stream_manager.lookup_stream(n.get_int_value()) 2150 2151 if stream is None or stream.io.closed: 2152 evaluation.message("Put", "openx", Expression("OutputSteam", name, n)) 2153 return 2154 2155 text = [ 2156 evaluation.format_output(Expression("InputForm", expr)) 2157 for expr in exprs.get_sequence() 2158 ] 2159 text = "\n".join(text) + "\n" 2160 text.encode("utf-8") 2161 2162 stream.io.write(text) 2163 2164 return SymbolNull 2165 2166 def apply_default(self, exprs, filename, evaluation): 2167 "Put[exprs___, filename_]" 2168 expr = Expression("Put", exprs, filename) 2169 evaluation.message("General", "stream", filename) 2170 return expr 2171 2172 2173class PutAppend(BinaryOperator): 2174 """ 2175 <dl> 2176 <dt>'$expr$ >>> $filename$' 2177 <dd>append $expr$ to a file. 2178 <dt>'PutAppend[$expr1$, $expr2$, ..., $"filename"$]' 2179 <dd>write a sequence of expressions to a file. 2180 </dl> 2181 2182 >> Put[50!, "factorials"] 2183 >> FilePrint["factorials"] 2184 | 30414093201713378043612608166064768844377641568960512000000000000 2185 2186 >> PutAppend[10!, 20!, 30!, "factorials"] 2187 >> FilePrint["factorials"] 2188 | 30414093201713378043612608166064768844377641568960512000000000000 2189 | 3628800 2190 | 2432902008176640000 2191 | 265252859812191058636308480000000 2192 2193 >> 60! >>> "factorials" 2194 >> FilePrint["factorials"] 2195 | 30414093201713378043612608166064768844377641568960512000000000000 2196 | 3628800 2197 | 2432902008176640000 2198 | 265252859812191058636308480000000 2199 | 8320987112741390144276341183223364380754172606361245952449277696409600000000000000 2200 2201 >> "string" >>> factorials 2202 >> FilePrint["factorials"] 2203 | 30414093201713378043612608166064768844377641568960512000000000000 2204 | 3628800 2205 | 2432902008176640000 2206 | 265252859812191058636308480000000 2207 | 8320987112741390144276341183223364380754172606361245952449277696409600000000000000 2208 | "string" 2209 #> DeleteFile["factorials"]; 2210 2211 ## writing to dir 2212 #> x >>> /var/ 2213 : Cannot open /var/. 2214 = x >>> /var/ 2215 2216 ## writing to read only file 2217 #> x >>> /proc/uptime 2218 : Cannot open /proc/uptime. 2219 = x >>> /proc/uptime 2220 """ 2221 2222 operator = ">>>" 2223 precedence = 30 2224 attributes = "Protected" 2225 2226 def apply(self, exprs, filename, evaluation): 2227 "PutAppend[exprs___, filename_String]" 2228 instream = Expression("OpenAppend", filename).evaluate(evaluation) 2229 if len(instream.leaves) == 2: 2230 name, n = instream.leaves 2231 else: 2232 return # opening failed 2233 result = self.apply_input(exprs, name, n, evaluation) 2234 Expression("Close", instream).evaluate(evaluation) 2235 return result 2236 2237 def apply_input(self, exprs, name, n, evaluation): 2238 "PutAppend[exprs___, OutputStream[name_, n_]]" 2239 stream = stream_manager.lookup_stream(n.get_int_value()) 2240 2241 if stream is None or stream.io.closed: 2242 evaluation.message("Put", "openx", Expression("OutputSteam", name, n)) 2243 return 2244 2245 text = [ 2246 str(e.do_format(evaluation, "System`OutputForm").__str__()) 2247 for e in exprs.get_sequence() 2248 ] 2249 text = "\n".join(text) + "\n" 2250 text.encode("ascii") 2251 2252 stream.io.write(text) 2253 2254 return SymbolNull 2255 2256 def apply_default(self, exprs, filename, evaluation): 2257 "PutAppend[exprs___, filename_]" 2258 expr = Expression("PutAppend", exprs, filename) 2259 evaluation.message("General", "stream", filename) 2260 return expr 2261 2262 2263class ReadList(Read): 2264 """ 2265 <dl> 2266 <dt>'ReadList["$file$"]' 2267 <dd>Reads all the expressions until the end of file. 2268 <dt>'ReadList["$file$", $type$]' 2269 <dd>Reads objects of a specified type until the end of file. 2270 <dt>'ReadList["$file$", {$type1$, $type2$, ...}]' 2271 <dd>Reads a sequence of specified types until the end of file. 2272 </dl> 2273 2274 >> ReadList[StringToStream["a 1 b 2"], {Word, Number}] 2275 = {{a, 1}, {b, 2}} 2276 2277 >> stream = StringToStream["\\"abc123\\""]; 2278 >> ReadList[stream] 2279 = {abc123} 2280 >> InputForm[%] 2281 = {"abc123"} 2282 2283 #> ReadList[stream, "Invalid"] 2284 : Invalid is not a valid format specification. 2285 = ReadList[..., Invalid] 2286 #> Close[stream]; 2287 2288 2289 #> ReadList[StringToStream["a 1 b 2"], {Word, Number}, 1] 2290 = {{a, 1}} 2291 """ 2292 2293 # TODO 2294 """ 2295 #> ReadList[StringToStream["a 1 b 2"], {Word, Number}, -1] 2296 : Non-negative machine-sized integer expected at position 3 in ReadList[InputStream[String, ...], {Word, Number}, -1]. 2297 = ReadList[InputStream[String, ...], {Word, Number}, -1] 2298 """ 2299 2300 # TODO: Expression type 2301 """ 2302 #> ReadList[StringToStream["123 45 x y"], Expression] 2303 = {5535 x y} 2304 """ 2305 2306 # TODO: Accept newlines in input 2307 """ 2308 >> ReadList[StringToStream["123\nabc"]] 2309 = {123, abc} 2310 >> InputForm[%] 2311 = {123, abc} 2312 """ 2313 2314 rules = { 2315 "ReadList[stream_]": "ReadList[stream, Expression]", 2316 } 2317 2318 attributes = "Protected" 2319 2320 options = { 2321 "NullRecords": "False", 2322 "NullWords": "False", 2323 "RecordSeparators": '{"\r\n", "\n", "\r"}', 2324 "TokenWords": "{}", 2325 "WordSeparators": '{" ", "\t"}', 2326 } 2327 2328 def apply(self, channel, types, evaluation, options): 2329 "ReadList[channel_, types_, OptionsPattern[ReadList]]" 2330 2331 # Options 2332 # TODO: Implement extra options 2333 # py_options = self.check_options(options) 2334 # null_records = py_options['NullRecords'] 2335 # null_words = py_options['NullWords'] 2336 # record_separators = py_options['RecordSeparators'] 2337 # token_words = py_options['TokenWords'] 2338 # word_separators = py_options['WordSeparators'] 2339 2340 result = [] 2341 while True: 2342 tmp = super(ReadList, self).apply(channel, types, evaluation, options) 2343 2344 if tmp is None: 2345 return 2346 2347 if tmp == SymbolFailed: 2348 return 2349 2350 if tmp == SymbolEndOfFile: 2351 break 2352 result.append(tmp) 2353 return from_python(result) 2354 2355 def apply_m(self, channel, types, m, evaluation, options): 2356 "ReadList[channel_, types_, m_, OptionsPattern[ReadList]]" 2357 2358 # Options 2359 # TODO: Implement extra options 2360 # py_options = self.check_options(options) 2361 # null_records = py_options['NullRecords'] 2362 # null_words = py_options['NullWords'] 2363 # record_separators = py_options['RecordSeparators'] 2364 # token_words = py_options['TokenWords'] 2365 # word_separators = py_options['WordSeparators'] 2366 2367 py_m = m.get_int_value() 2368 if py_m < 0: 2369 evaluation.message( 2370 "ReadList", "intnm", Expression("ReadList", channel, types, m) 2371 ) 2372 return 2373 2374 result = [] 2375 for i in range(py_m): 2376 tmp = super(ReadList, self).apply(channel, types, evaluation, options) 2377 2378 if tmp == SymbolFailed: 2379 return 2380 2381 if tmp.to_python() == "EndOfFile": 2382 break 2383 result.append(tmp) 2384 return from_python(result) 2385 2386 2387class FilePrint(Builtin): 2388 """ 2389 <dl> 2390 <dt>'FilePrint[$file$]' 2391 <dd>prints the raw contents of $file$. 2392 </dl> 2393 2394 #> exp = Sin[1]; 2395 #> FilePrint[exp] 2396 : File specification Sin[1] is not a string of one or more characters. 2397 = FilePrint[Sin[1]] 2398 2399 #> FilePrint["somenonexistantpath_h47sdmk^&h4"] 2400 : Cannot open somenonexistantpath_h47sdmk^&h4. 2401 = FilePrint[somenonexistantpath_h47sdmk^&h4] 2402 2403 #> FilePrint[""] 2404 : File specification is not a string of one or more characters. 2405 = FilePrint[] 2406 """ 2407 2408 messages = { 2409 "fstr": ( 2410 "File specification `1` is not a string of " "one or more characters." 2411 ), 2412 } 2413 2414 options = { 2415 "CharacterEncoding": "$CharacterEncoding", 2416 "RecordSeparators": '{"\r\n", "\n", "\r"}', 2417 "WordSeparators": '{" ", "\t"}', 2418 } 2419 2420 attributes = "Protected" 2421 2422 def apply(self, path, evaluation, options): 2423 "FilePrint[path_ OptionsPattern[FilePrint]]" 2424 pypath = path.to_python() 2425 if not ( 2426 isinstance(pypath, str) 2427 and pypath[0] == pypath[-1] == '"' 2428 and len(pypath) > 2 2429 ): 2430 evaluation.message("FilePrint", "fstr", path) 2431 return 2432 pypath = path_search(pypath[1:-1]) 2433 2434 # Options 2435 record_separators = options["System`RecordSeparators"].to_python() 2436 assert isinstance(record_separators, list) 2437 assert all( 2438 isinstance(s, str) and s[0] == s[-1] == '"' for s in record_separators 2439 ) 2440 record_separators = [s[1:-1] for s in record_separators] 2441 2442 if pypath is None: 2443 evaluation.message("General", "noopen", path) 2444 return 2445 2446 if not osp.isfile(pypath): 2447 return SymbolFailed 2448 2449 try: 2450 with mathics_open(pypath, "r") as f: 2451 result = f.read() 2452 except IOError: 2453 evaluation.message("General", "noopen", path) 2454 return 2455 except MessageException as e: 2456 e.message(evaluation) 2457 return 2458 2459 result = [result] 2460 for sep in record_separators: 2461 result = [item for res in result for item in res.split(sep)] 2462 2463 if result[-1] == "": 2464 result = result[:-1] 2465 2466 for res in result: 2467 evaluation.print_out(from_python(res)) 2468 2469 return SymbolNull 2470 2471 2472class Close(Builtin): 2473 """ 2474 <dl> 2475 <dt>'Close[$stream$]' 2476 <dd>closes an input or output stream. 2477 </dl> 2478 2479 >> Close[StringToStream["123abc"]] 2480 = String 2481 2482 >> Close[OpenWrite[]] 2483 = ... 2484 2485 #> Streams[] == (Close[OpenWrite[]]; Streams[]) 2486 = True 2487 2488 #> Close["abc"] 2489 : abc is not open. 2490 = Close[abc] 2491 2492 #> strm = OpenWrite[]; 2493 #> Close[strm]; 2494 #> Quiet[Close[strm]] 2495 = Close[OutputStream[...]] 2496 """ 2497 2498 attributes = "Protected" 2499 2500 messages = { 2501 "closex": "`1`.", 2502 } 2503 2504 def apply(self, channel, evaluation): 2505 "Close[channel_]" 2506 2507 if channel.has_form(("InputStream", "OutputStream"), 2): 2508 [name, n] = channel.get_leaves() 2509 stream = stream_manager.lookup_stream(n.get_int_value()) 2510 else: 2511 stream = None 2512 2513 if stream is None or stream.io is None or stream.io.closed: 2514 evaluation.message("General", "openx", channel) 2515 return 2516 2517 stream.io.close() 2518 return name 2519 2520 2521class StreamPosition(Builtin): 2522 """ 2523 <dl> 2524 <dt>'StreamPosition[$stream$]' 2525 <dd>returns the current position in a stream as an integer. 2526 </dl> 2527 2528 >> stream = StringToStream["Mathics is cool!"] 2529 = ... 2530 2531 >> Read[stream, Word] 2532 = Mathics 2533 2534 >> StreamPosition[stream] 2535 = 7 2536 """ 2537 2538 attributes = "Protected" 2539 2540 def apply_input(self, name, n, evaluation): 2541 "StreamPosition[InputStream[name_, n_]]" 2542 stream = stream_manager.lookup_stream(n.get_int_value()) 2543 2544 if stream is None or stream.io is None or stream.io.closed: 2545 evaluation.message("General", "openx", name) 2546 return 2547 2548 return from_python(stream.io.tell()) 2549 2550 def apply_output(self, name, n, evaluation): 2551 "StreamPosition[OutputStream[name_, n_]]" 2552 self.input_apply(name, n, evaluation) 2553 2554 def apply_default(self, stream, evaluation): 2555 "StreamPosition[stream_]" 2556 evaluation.message("General", "stream", stream) 2557 return 2558 2559 2560class SetStreamPosition(Builtin): 2561 """ 2562 <dl> 2563 <dt>'SetStreamPosition[$stream$, $n$]' 2564 <dd>sets the current position in a stream. 2565 </dl> 2566 2567 >> stream = StringToStream["Mathics is cool!"] 2568 = ... 2569 2570 >> SetStreamPosition[stream, 8] 2571 = 8 2572 2573 >> Read[stream, Word] 2574 = is 2575 2576 #> SetStreamPosition[stream, -5] 2577 : Python2 cannot handle negative seeks. 2578 = 10 2579 2580 >> SetStreamPosition[stream, Infinity] 2581 = 16 2582 """ 2583 2584 # TODO: Seeks beyond stream should return stmrng message 2585 """ 2586 #> SetStreamPosition[stream, 40] 2587 = ERROR_MESSAGE_HERE 2588 """ 2589 2590 messages = { 2591 "int": "Integer expected at position 2 in `1`.", 2592 "stmrng": ( 2593 "Cannot set the current point in stream `1` to position `2`. The " 2594 "requested position exceeds the number of characters in the file" 2595 ), 2596 "python2": "Python2 cannot handle negative seeks.", # FIXME: Python3? 2597 } 2598 2599 attributes = "Protected" 2600 2601 def apply_input(self, name, n, m, evaluation): 2602 "SetStreamPosition[InputStream[name_, n_], m_]" 2603 stream = stream_manager.lookup_stream(n.get_int_value()) 2604 2605 if stream is None or stream.io is None or stream.io.closed: 2606 evaluation.message("General", "openx", name) 2607 return 2608 2609 if not stream.io.seekable: 2610 raise NotImplementedError 2611 2612 seekpos = m.to_python() 2613 if not (isinstance(seekpos, int) or seekpos == float("inf")): 2614 evaluation.message( 2615 "SetStreamPosition", "stmrng", Expression("InputStream", name, n), m 2616 ) 2617 return 2618 2619 try: 2620 if seekpos == float("inf"): 2621 stream.io.seek(0, 2) 2622 else: 2623 if seekpos < 0: 2624 stream.io.seek(seekpos, 2) 2625 else: 2626 stream.io.seek(seekpos) 2627 except IOError: 2628 evaluation.message("SetStreamPosition", "python2") 2629 2630 return from_python(stream.io.tell()) 2631 2632 def apply_output(self, name, n, m, evaluation): 2633 "SetStreamPosition[OutputStream[name_, n_], m_]" 2634 return self.apply_input(name, n, m, evaluation) 2635 2636 def apply_default(self, stream, evaluation): 2637 "SetStreamPosition[stream_]" 2638 evaluation.message("General", "stream", stream) 2639 return 2640 2641 2642class Skip(Read): 2643 """ 2644 <dl> 2645 <dt>'Skip[$stream$, $type$]' 2646 <dd>skips ahead in an input steream by one object of the specified $type$. 2647 <dt>'Skip[$stream$, $type$, $n$]' 2648 <dd>skips ahead in an input steream by $n$ objects of the specified $type$. 2649 </dl> 2650 2651 >> stream = StringToStream["a b c d"]; 2652 >> Read[stream, Word] 2653 = a 2654 >> Skip[stream, Word] 2655 >> Read[stream, Word] 2656 = c 2657 #> Close[stream]; 2658 2659 >> stream = StringToStream["a b c d"]; 2660 >> Read[stream, Word] 2661 = a 2662 >> Skip[stream, Word, 2] 2663 >> Read[stream, Word] 2664 = d 2665 #> Skip[stream, Word] 2666 = EndOfFile 2667 #> Close[stream]; 2668 """ 2669 2670 rules = { 2671 "Skip[InputStream[name_, n_], types_]": "Skip[InputStream[name, n], types, 1]", 2672 } 2673 2674 messages = { 2675 "intm": "Non-negative machine-sized integer expected at position 3 in `1`", 2676 } 2677 2678 options = { 2679 "AnchoredSearch": "False", 2680 "IgnoreCase": "False", 2681 "WordSearch": "False", 2682 "RecordSeparators": '{"\r\n", "\n", "\r"}', 2683 "WordSeparators": '{" ", "\t"}', 2684 } 2685 2686 attributes = "Protected" 2687 2688 def apply(self, name, n, types, m, evaluation, options): 2689 "Skip[InputStream[name_, n_], types_, m_, OptionsPattern[Skip]]" 2690 2691 channel = Expression("InputStream", name, n) 2692 2693 # Options 2694 # TODO Implement extra options 2695 # py_options = self.check_options(options) 2696 # null_records = py_options['NullRecords'] 2697 # null_words = py_options['NullWords'] 2698 # record_separators = py_options['RecordSeparators'] 2699 # token_words = py_options['TokenWords'] 2700 # word_separators = py_options['WordSeparators'] 2701 2702 py_m = m.to_python() 2703 if not (isinstance(py_m, int) and py_m > 0): 2704 evaluation.message( 2705 "Skip", 2706 "intm", 2707 Expression("Skip", Expression("InputStream", name, n), types, m), 2708 ) 2709 return 2710 for i in range(py_m): 2711 result = super(Skip, self).apply(channel, types, evaluation, options) 2712 if result == SymbolEndOfFile: 2713 return result 2714 return SymbolNull 2715 2716 2717class Find(Read): 2718 """ 2719 <dl> 2720 <dt>'Find[$stream$, $text$]' 2721 <dd>find the first line in $stream$ that contains $text$. 2722 </dl> 2723 2724 >> stream = OpenRead["ExampleData/EinsteinSzilLetter.txt"]; 2725 >> Find[stream, "uranium"] 2726 = in manuscript, leads me to expect that the element uranium may be turned into 2727 >> Find[stream, "uranium"] 2728 = become possible to set up a nuclear chain reaction in a large mass of uranium, 2729 >> Close[stream] 2730 = ... 2731 2732 >> stream = OpenRead["ExampleData/EinsteinSzilLetter.txt"]; 2733 >> Find[stream, {"energy", "power"} ] 2734 = a new and important source of energy in the immediate future. Certain aspects 2735 >> Find[stream, {"energy", "power"} ] 2736 = by which vast amounts of power and large quantities of new radium-like 2737 >> Close[stream] 2738 = ... 2739 """ 2740 2741 attributes = "Protected" 2742 2743 options = { 2744 "AnchoredSearch": "False", 2745 "IgnoreCase": "False", 2746 "WordSearch": "False", 2747 "RecordSeparators": '{"\r\n", "\n", "\r"}', 2748 "WordSeparators": '{" ", "\t"}', 2749 } 2750 2751 def apply(self, name, n, text, evaluation, options): 2752 "Find[InputStream[name_, n_], text_, OptionsPattern[Find]]" 2753 2754 # Options 2755 # TODO Implement extra options 2756 # py_options = self.check_options(options) 2757 # anchored_search = py_options['AnchoredSearch'] 2758 # ignore_case = py_options['IgnoreCase'] 2759 # word_search = py_options['WordSearch'] 2760 # record_separators = py_options['RecordSeparators'] 2761 # word_separators = py_options['WordSeparators'] 2762 2763 py_text = text.to_python() 2764 2765 channel = Expression("InputStream", name, n) 2766 2767 if not isinstance(py_text, list): 2768 py_text = [py_text] 2769 2770 if not all(isinstance(t, str) and t[0] == t[-1] == '"' for t in py_text): 2771 evaluation.message("Find", "unknown", Expression("Find", channel, text)) 2772 return 2773 2774 py_text = [t[1:-1] for t in py_text] 2775 2776 while True: 2777 tmp = super(Find, self).apply( 2778 channel, Symbol("Record"), evaluation, options 2779 ) 2780 py_tmp = tmp.to_python()[1:-1] 2781 2782 if py_tmp == "System`EndOfFile": 2783 evaluation.message( 2784 "Find", "notfound", Expression("Find", channel, text) 2785 ) 2786 return SymbolFailed 2787 2788 for t in py_text: 2789 if py_tmp.find(t) != -1: 2790 return from_python(py_tmp) 2791 2792 2793class InputStream(Builtin): 2794 """ 2795 <dl> 2796 <dt>'InputStream[$name$, $n$]' 2797 <dd>represents an input stream. 2798 </dl> 2799 2800 >> stream = StringToStream["Mathics is cool!"] 2801 = ... 2802 >> Close[stream] 2803 = String 2804 """ 2805 2806 attributes = "Protected" 2807 2808 def apply(self, name, n, evaluation): 2809 "InputStream[name_, n_]" 2810 return 2811 2812 2813class OutputStream(Builtin): 2814 """ 2815 <dl> 2816 <dt>'OutputStream[$name$, $n$]' 2817 <dd>represents an output stream. 2818 </dl> 2819 2820 >> OpenWrite[] 2821 = ... 2822 >> Close[%] 2823 = ... 2824 """ 2825 2826 attributes = "Protected" 2827 2828 def apply(self, name, n, evaluation): 2829 "OutputStream[name_, n_]" 2830 return 2831 2832 2833class StringToStream(Builtin): 2834 """ 2835 <dl> 2836 <dt>'StringToStream[$string$]' 2837 <dd>converts a $string$ to an open input stream. 2838 </dl> 2839 2840 >> strm = StringToStream["abc 123"] 2841 = InputStream[String, ...] 2842 2843 #> Read[strm, Word] 2844 = abc 2845 2846 #> Read[strm, Number] 2847 = 123 2848 2849 #> Close[strm] 2850 = String 2851 """ 2852 2853 attributes = "Protected" 2854 2855 def apply(self, string, evaluation): 2856 "StringToStream[string_]" 2857 pystring = string.to_python()[1:-1] 2858 fp = io.StringIO(str(pystring)) 2859 2860 name = Symbol("String") 2861 stream = stream_manager.add(pystring, io=fp) 2862 return Expression("InputStream", name, Integer(stream.n)) 2863 2864 2865class Streams(Builtin): 2866 """ 2867 <dl> 2868 <dt>'Streams[]' 2869 <dd>returns a list of all open streams. 2870 </dl> 2871 2872 >> Streams[] 2873 = ... 2874 2875 >> Streams["stdout"] 2876 = ... 2877 2878 #> OpenWrite[] 2879 = ... 2880 #> Streams[%[[1]]] 2881 = {OutputStream[...]} 2882 2883 #> Streams["some_nonexistant_name"] 2884 = {} 2885 """ 2886 2887 attributes = "Protected" 2888 2889 def apply(self, evaluation): 2890 "Streams[]" 2891 return self.apply_name(None, evaluation) 2892 2893 def apply_name(self, name, evaluation): 2894 "Streams[name_String]" 2895 result = [] 2896 for stream in stream_manager.STREAMS.values(): 2897 if stream is None or stream.io.closed: 2898 continue 2899 if isinstance(stream.io, io.StringIO): 2900 head = "InputStream" 2901 _name = Symbol("String") 2902 else: 2903 mode = stream.mode 2904 if mode in ["r", "rb"]: 2905 head = "InputStream" 2906 elif mode in ["w", "a", "wb", "ab"]: 2907 head = "OutputStream" 2908 else: 2909 raise ValueError("Unknown mode {0}".format(mode)) 2910 _name = String(stream.name) 2911 expr = Expression(head, _name, Integer(stream.n)) 2912 if name is None or _name == name: 2913 result.append(expr) 2914 return Expression("List", *result) 2915