From 90c3f3fee9838b2e44b8e6f198d09625a603d7b0 Mon Sep 17 00:00:00 2001 From: TabulateJarl8 Date: Wed, 14 Jul 2021 14:39:44 -0400 Subject: [PATCH] add stdin support --- README.md | 19 +++++++++++++++-- ti842py/__version__.py | 2 +- ti842py/main.py | 46 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7a33e7c..86b145b 100644 --- a/README.md +++ b/README.md @@ -62,15 +62,17 @@ ti842py can be installed via PyPI or by cloning the repository. To install it wi Feel free to use the programs found at [https://github.com/TabulateJarl8/tiprograms](https://github.com/TabulateJarl8/tiprograms) to test this project out. +## CLI Usage + ti842py can be used in 3 different ways. The first way is just running it from the command line. For example, if you wanted to convert the program in `tiprogram.txt` to `tiprogram.py`, you can this command: `ti842py tiprogram.txt -o tiprogram.py`. If no value is specified for `-o`, the converted program will be written to `stdout`. The `-n` flag can be added to force the transpiler to not decompile the input file, and the `-d` flag can be added to force the transpiler to attempt and decompile the input file. If the `--run` or `-r` argument is supplied, the resulting python file will be run after it is done transpiling ``` -usage: ti842py [-h] [-o OUTFILE] [-n] [-d] [--no-fix-multiplication] [-r] [-V] infile +usage: ti842py [-h] [-o OUTFILE] [-n] [-d] [--no-fix-multiplication] [--no-fix-floating-point] [--turbo-draw] [-r] [-V] [infile] TI-BASIC to Python 3 Transpiler positional arguments: - infile Input file. + infile Input file (filename or stdin). optional arguments: -h, --help show this help message and exit @@ -88,6 +90,19 @@ optional arguments: -V, --version show program's version number and exit ``` +### Advanced terminal usage + +You can use ti842py by piping files to it's stdin. This can be done via pipes or redirects, and the files can be either 8Xp or plain text files, just like normal. Here's some examples: + +```sh +$ cat BAR.8Xp | ti842py --run +$ ti842py -o bar.py < BAR.8Xp +$ cat CLOCK.bas | ti842py -nr +$ ti842py --no-fix-floating-point --run < CLOCK.bas +``` + +## Programmatic Usage + ti842py can also be imported and used in a program. Here is an example program to convert `tiprogram.txt` to `tiprogram.py`: ```py diff --git a/ti842py/__version__.py b/ti842py/__version__.py index 4fb3ae2..25f942e 100644 --- a/ti842py/__version__.py +++ b/ti842py/__version__.py @@ -1,7 +1,7 @@ __title__ = "ti842py" __description__ = "TI-BASIC to Python 3 Transpiler" __url__ = "https://github.com/TabulateJarl8/ti842py" -__version__ = "0.7.9" +__version__ = "0.8.0" __author__ = "Tabulate" __author_email__ = "tabulatejarl8@gmail.com" __license__ = "GPLv3" diff --git a/ti842py/main.py b/ti842py/main.py index ba64cb3..9a7d38f 100644 --- a/ti842py/main.py +++ b/ti842py/main.py @@ -4,6 +4,8 @@ import tempfile import sys import subprocess +import io +import pty try: from .tiParser import TIBasicParser @@ -29,6 +31,16 @@ def isUTF8(file): def transpile(infile, outfile="stdout", decompileFile=True, forceDecompile=False, multiplication=True, floating_point=True, turbo_draw=False, run=False): + # detect stdin + if not os.path.exists(infile.name): + # Don't auto close since we are shadowing infile + temp_stdin = tempfile.NamedTemporaryFile() + temp_stdin.write(infile.buffer.read()) + temp_stdin.seek(0) + infile = temp_stdin.name + else: + infile = infile.name + decode = not isUTF8(infile) and decompileFile is True if decode is True or forceDecompile is True: @@ -47,6 +59,10 @@ def transpile(infile, outfile="stdout", decompileFile=True, forceDecompile=False pythonCode = TIBasicParser(file_lines, multiplication, floating_point, turbo_draw).toPython() + # Close temp_stdin if used + if 'temp_stdin' in vars() or 'temp_stdin' in globals(): + temp_stdin.close() + # Write to outfile if outfile == "stdout": if run is False: @@ -56,7 +72,16 @@ def transpile(infile, outfile="stdout", decompileFile=True, forceDecompile=False for line in pythonCode: f.write(line.encode() + b"\n") f.seek(0) - proc = subprocess.Popen([sys.executable, f.name]) + + # test if something was piped to stdin + if 'temp_stdin' in vars() or 'temp_stdin' in globals(): + # Create new tty to handle ioctl errors in termios + master_fd, slave_fd = pty.openpty() + proc = subprocess.Popen([sys.executable, f.name], stdin=slave_fd) + else: + # Nothing was piped to stdin + proc = subprocess.Popen([sys.executable, f.name]) + try: proc.wait() except (Exception, KeyboardInterrupt): @@ -71,11 +96,13 @@ def transpile(infile, outfile="stdout", decompileFile=True, forceDecompile=False def main(): parser = argparse.ArgumentParser(description='TI-BASIC to Python 3 Transpiler') - parser.add_argument( + infile_argument = parser.add_argument( 'infile', metavar='infile', - nargs=1, - help="Input file." + nargs='?', + type=argparse.FileType('r'), + default=(None if sys.stdin.isatty() else sys.stdin), + help="Input file (filename or stdin)." ) parser.add_argument( '-o', @@ -136,7 +163,16 @@ def main(): ) args = parser.parse_args() - transpile(args.infile[0], args.outfile, args.n, args.d, args.multiplication, args.floating_point, args.turbo_draw, args.run) + + if hasattr(args.infile, '__getitem__'): + infile = args.infile[0] + else: + infile = args.infile + + if infile is None: + raise argparse.ArgumentError(infile_argument, 'the infile argument is required') + + transpile(infile, args.outfile, args.n, args.d, args.multiplication, args.floating_point, args.turbo_draw, args.run) if __name__ == "__main__":