├── rockstarpy ├── __init__.py ├── __main__.py ├── command_line.py └── transpile.py ├── tests ├── testfunccall.numbers.py ├── testfunccall.numbers.rock ├── testfunccall.variables.py ├── testfunccall.variables.rock ├── testglobal.py ├── testglobal.rock ├── fizz.py ├── fizz.rock ├── test_pronouns.py ├── test_variables.py ├── run_tests.py ├── test_calculation.py ├── test_conditionals.py ├── test_blocks.py └── test_assignment.py ├── .travis.yml ├── LICENSE ├── setup.py ├── .gitignore ├── .github └── workflows │ └── codeql-analysis.yml └── README.md /rockstarpy/__init__.py: -------------------------------------------------------------------------------- 1 | from .transpile import Transpiler 2 | -------------------------------------------------------------------------------- /tests/testfunccall.numbers.py: -------------------------------------------------------------------------------- 1 | def Multiply(Love, Life): 2 | return Love * Life 3 | print(Multiply(3, 444)) 4 | -------------------------------------------------------------------------------- /tests/testfunccall.numbers.rock: -------------------------------------------------------------------------------- 1 | Multiply takes Love and Life 2 | Give back Love of Life 3 | 4 | Say Multiply taking 3, 444 5 | -------------------------------------------------------------------------------- /rockstarpy/__main__.py: -------------------------------------------------------------------------------- 1 | from rockstarpy.command_line import command_line 2 | 3 | if __name__ == "__main__": 4 | command_line() -------------------------------------------------------------------------------- /tests/testfunccall.variables.py: -------------------------------------------------------------------------------- 1 | def Multiply(Love, Life): 2 | return Love * Life 3 | my_heart = 1 4 | the_devil = 666 5 | Multiply(my_heart, the_devil) 6 | -------------------------------------------------------------------------------- /tests/testfunccall.variables.rock: -------------------------------------------------------------------------------- 1 | Multiply takes Love and Life 2 | Give back Love of Life 3 | 4 | Put 1 into my heart 5 | Put 666 into the devil 6 | Multiply taking my heart, the devil 7 | -------------------------------------------------------------------------------- /tests/testglobal.py: -------------------------------------------------------------------------------- 1 | Desire = 100 2 | print(Desire) 3 | def Midnight(your_heart, your_soul): 4 | global Desire 5 | Desire = 5 6 | print(Desire) 7 | return your_heart 8 | print(Desire) 9 | Midnight(5, 2) 10 | print(Desire) 11 | -------------------------------------------------------------------------------- /tests/testglobal.rock: -------------------------------------------------------------------------------- 1 | Desire's a lovestruck ladykiller. 2 | Shout Desire 3 | 4 | Midnight takes your heart and your soul; 5 | Desire is water 6 | Shout Desire 7 | Give back your heart, 8 | 9 | Shout Desire 10 | Midnight taking 5, 2. 11 | Shout Desire -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | dist: bionic 3 | 4 | python: 5 | - "3.6" 6 | - "3.7" 7 | - "3.8" 8 | - "3.9-dev" 9 | - "3.10-dev" 10 | - "nightly" 11 | 12 | jobs: 13 | allow_failures: 14 | - python: "nightly" 15 | 16 | install: 17 | - python setup.py install 18 | 19 | script: 20 | - python tests/run_tests.py 21 | 22 | -------------------------------------------------------------------------------- /tests/fizz.py: -------------------------------------------------------------------------------- 1 | def Midnight(your_heart, your_soul): 2 | while your_heart >= your_soul: # this is a comment 3 | your_heart = your_heart - your_soul 4 | return your_heart 5 | Desire = 100 6 | my_world = False 7 | Fire = 3 # i love comments 8 | Hate = 5 9 | while not my_world == Desire: 10 | my_world += 1 11 | if Midnight(my_world, Fire) == False and Midnight(my_world, Hate) == False: 12 | print("FizzBuzz!") 13 | continue 14 | if Midnight(my_world, Fire) == False: 15 | print("Fizz!") 16 | continue 17 | if Midnight(my_world, Hate) == False: 18 | print("Buzz!") 19 | continue 20 | print(my_world) 21 | -------------------------------------------------------------------------------- /tests/fizz.rock: -------------------------------------------------------------------------------- 1 | Midnight takes your heart and your soul 2 | While your heart is as high as your soul (this is a comment) 3 | Put your heart without your soul into your heart 4 | 5 | Give back your heart 6 | 7 | 8 | Desire's a lovestruck ladykiller 9 | My world is empty 10 | Fire's ice (i love comments) 11 | Hate is water 12 | Until my world is Desire, 13 | Build my world up 14 | If Midnight taking my world, Fire is nothing and Midnight taking my world, Hate is nothing 15 | Shout "FizzBuzz!" 16 | Take it to the top 17 | 18 | If Midnight taking my world, Fire is nothing 19 | Shout "Fizz!" 20 | Take it to the top 21 | 22 | If Midnight taking my world, Hate is nothing 23 | Say "Buzz!" 24 | Take it to the top 25 | 26 | Whisper my world 27 | -------------------------------------------------------------------------------- /tests/test_pronouns.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | 5 | sys.path = [os.path.dirname(os.path.dirname(os.path.realpath(__file__)))] + sys.path 6 | from rockstarpy.transpile import Transpiler 7 | 8 | 9 | class TestPronouns(unittest.TestCase): 10 | def test_it_if(self): 11 | rock = ["Green is 0\n", "Build it up\n"] 12 | 13 | py = ["Green = 0\n", "Green += 1\n"] 14 | 15 | self.__run_block_test(rock, py) 16 | 17 | @unittest.skip("Pronouns are not working in calculations") 18 | def test_it_until(self): 19 | rock = ["The day is 0\n", "Until the day is 7\n", "Build it up\n"] 20 | 21 | py = ["the_day = 0\n", "while not the_day == 7:\n", " the_day += 1\n"] 22 | 23 | self.__run_block_test(rock, py) 24 | 25 | def __run_block_test(self, rock, py): 26 | transpiler = Transpiler() 27 | transpiled = [transpiler.transpile_line(line) for line in rock] 28 | self.assertEqual(len(py), len(transpiled)) 29 | for py_line, transpiled_line in zip(py, transpiled): 30 | self.assertEqual(py_line, transpiled_line) 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Yan Orestes 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/test_variables.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | 5 | sys.path = [os.path.dirname(os.path.dirname(os.path.realpath(__file__)))] + sys.path 6 | from rockstarpy.transpile import Transpiler 7 | 8 | 9 | class TestVariables(unittest.TestCase): 10 | def test_simple_variables(self): 11 | transpiler = Transpiler() 12 | py_line = transpiler.transpile_line("X is 2\n") 13 | self.assertEqual(py_line, "X = 2\n") 14 | 15 | py_line = transpiler.transpile_line("Tommy is a rockstar\n") 16 | self.assertEqual(py_line, "Tommy = 18\n") 17 | 18 | def test_common_variables(self): 19 | transpiler = Transpiler() 20 | py_line = transpiler.transpile_line("My variable is 5\n") 21 | self.assertEqual(py_line, "my_variable = 5\n") 22 | 23 | py_line = transpiler.transpile_line("Shout the total\n") 24 | self.assertEqual(py_line, "print(the_total)\n") 25 | 26 | def test_proper_variables(self): 27 | transpiler = Transpiler() 28 | py_line = transpiler.transpile_line("Master Of The Universe is nothing\n") 29 | self.assertEqual(py_line, "Master_Of_The_Universe = False\n") 30 | 31 | 32 | if __name__ == "__main__": 33 | unittest.main() 34 | -------------------------------------------------------------------------------- /tests/run_tests.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import sys 3 | import difflib 4 | import unittest 5 | 6 | test_path = Path(__file__).resolve().parent 7 | sys.path.insert(0, str(test_path.parent)) 8 | from rockstarpy.transpile import Transpiler 9 | 10 | 11 | def check_files_identical(expected, actual): 12 | diff = difflib.unified_diff(expected, actual, fromfile="expected", tofile="actual",) 13 | line = None 14 | for line in diff: 15 | print(line, end="") 16 | if line is not None: 17 | print() 18 | assert False, "There are differences" 19 | 20 | def main(): 21 | identical_test_files = [ 22 | (file, file.with_suffix(".py")) 23 | for file in test_path.iterdir() 24 | if file.suffix == ".rock" 25 | ] 26 | for rock_path, py_path in identical_test_files: 27 | print("testing", rock_path.name) 28 | 29 | assert py_path.is_file(), f"{py_path} does not exist" 30 | 31 | transpiler = Transpiler() 32 | converted_code = "" 33 | with rock_path.open() as rockstar_file: 34 | for line in rockstar_file: 35 | converted_code += transpiler.transpile_line(line) 36 | 37 | with py_path.open() as expected_file: 38 | expected_code = expected_file.read() 39 | 40 | check_files_identical(expected_code, converted_code) 41 | 42 | suite = unittest.defaultTestLoader.discover(test_path, pattern="test_*.py") 43 | runner = unittest.TextTestRunner() 44 | runner.run(suite) 45 | 46 | 47 | if __name__ == "__main__": 48 | main() 49 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | with open("README.md") as file: 4 | long_description = file.read() 5 | 6 | setup( 7 | name="rockstar-py", 8 | version="2.1.0", 9 | author="yyyyyyyan", 10 | author_email="contact@yyyyyyyan.tech", 11 | packages=["rockstarpy"], 12 | description="Python transpiler for the esoteric language Rockstar", 13 | long_description=long_description, 14 | long_description_content_type="text/markdown", 15 | url="https://github.com/yyyyyyyan/rockstar-py", 16 | download_url="https://github.com/yyyyyyyan/rockstar-py/archive/2.1.0.zip", 17 | license="MIT", 18 | keywords="esoteric rockstar transpiler", 19 | classifiers=[ 20 | "Development Status :: 5 - Production/Stable", 21 | "Environment :: Console", 22 | "Intended Audience :: Developers", 23 | "License :: OSI Approved :: MIT License", 24 | "Natural Language :: English", 25 | "Operating System :: OS Independent", 26 | "Programming Language :: Python :: 3", 27 | "Programming Language :: Python :: 3 :: Only", 28 | "Programming Language :: Python :: 3.6", 29 | "Programming Language :: Python :: 3.7", 30 | "Programming Language :: Python :: 3.8", 31 | "Programming Language :: Python :: 3.9", 32 | "Topic :: Software Development :: Compilers", 33 | "Topic :: Software Development :: Interpreters", 34 | ], 35 | python_requires=">= 3.6", 36 | entry_points={ 37 | "console_scripts": ["rockstar-py=rockstarpy.command_line:command_line"] 38 | }, 39 | ) 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User 2 | TODO 3 | 4 | # PyCharm 5 | .idea/ 6 | 7 | # Byte-compiled / optimized / DLL files 8 | __pycache__/ 9 | *.py[cod] 10 | *$py.class 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | build/ 18 | develop-eggs/ 19 | dist/ 20 | downloads/ 21 | eggs/ 22 | .eggs/ 23 | lib/ 24 | lib64/ 25 | parts/ 26 | sdist/ 27 | var/ 28 | wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # pyenv 82 | .python-version 83 | 84 | # celery beat schedule file 85 | celerybeat-schedule 86 | 87 | # SageMath parsed files 88 | *.sage.py 89 | 90 | # Environments 91 | .env 92 | .venv 93 | env/ 94 | venv/ 95 | ENV/ 96 | env.bak/ 97 | venv.bak/ 98 | 99 | # Spyder project settings 100 | .spyderproject 101 | .spyproject 102 | 103 | # Rope project settings 104 | .ropeproject 105 | 106 | # mkdocs documentation 107 | /site 108 | 109 | # mypy 110 | .mypy_cache/ 111 | -------------------------------------------------------------------------------- /rockstarpy/command_line.py: -------------------------------------------------------------------------------- 1 | from rockstarpy.transpile import Transpiler 2 | from io import BytesIO 3 | import sys 4 | import argparse 5 | 6 | parser = argparse.ArgumentParser( 7 | description="Python transpiler for the esoteric language Rockstar" 8 | ) 9 | 10 | input_group = parser.add_mutually_exclusive_group(required=True) 11 | input_group.add_argument("-i", "--input", action="store", help="Input file (.rock)") 12 | input_group.add_argument( 13 | "--stdin", 14 | action="store_const", 15 | const=sys.stdin.buffer, 16 | help="Stream in stdin. Send EOF (Ctrl+D in *nix, Ctrl+Z in Windows) to stop", 17 | ) 18 | 19 | output_group = parser.add_mutually_exclusive_group() 20 | output_group.add_argument( 21 | "-o", "--output", action="store", help="Output file (.py)", default="output.py" 22 | ) 23 | output_group.add_argument( 24 | "--stdout", action="store_const", const=sys.stdout.buffer, help="Stream to stdout" 25 | ) 26 | output_group.add_argument( 27 | "--exec", action="store_true", help="Execute (without saving) the transpiled code " 28 | ) 29 | 30 | parser.add_argument( 31 | "-v", "--version", action="version", help="Version", version="2.1.0" 32 | ) 33 | 34 | args = parser.parse_args() 35 | 36 | def command_line(): 37 | sys.tracebacklimit = 0 38 | lyrics = args.stdin or open(args.input, "rb") 39 | output = BytesIO() if args.exec else args.stdout or open(args.output, "wb") 40 | 41 | transpiler = Transpiler() 42 | line_number = 1 43 | for line in lyrics: 44 | line = line.decode("utf8") 45 | try: 46 | output.write(transpiler.transpile_line(line).encode("utf8")) 47 | except SyntaxError as err: 48 | raise SyntaxError(err.msg + f":\n{line_number}.\t{line}") 49 | line_number += 1 50 | 51 | if args.exec: 52 | exec(output.getvalue()) 53 | if args.stdin is None: 54 | lyrics.close() 55 | if args.stdout is None: 56 | output.close() 57 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [master] 9 | schedule: 10 | - cron: '0 14 * * 4' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | # Override automatic language detection by changing the below list 21 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 22 | language: ['python'] 23 | # Learn more... 24 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection 25 | 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@v2 29 | with: 30 | # We must fetch at least the immediate parents so that if this is 31 | # a pull request then we can checkout the head. 32 | fetch-depth: 2 33 | 34 | # If this run was triggered by a pull request event, then checkout 35 | # the head of the pull request instead of the merge commit. 36 | - run: git checkout HEAD^2 37 | if: ${{ github.event_name == 'pull_request' }} 38 | 39 | # Initializes the CodeQL tools for scanning. 40 | - name: Initialize CodeQL 41 | uses: github/codeql-action/init@v1 42 | with: 43 | languages: ${{ matrix.language }} 44 | 45 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 46 | # If this step fails, then you should remove it and run the build manually (see below) 47 | - name: Autobuild 48 | uses: github/codeql-action/autobuild@v1 49 | 50 | # ℹ️ Command-line programs to run using the OS shell. 51 | # 📚 https://git.io/JvXDl 52 | 53 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 54 | # and modify them (or add more) to build your code if your project 55 | # uses a compiled language 56 | 57 | #- run: | 58 | # make bootstrap 59 | # make release 60 | 61 | - name: Perform CodeQL Analysis 62 | uses: github/codeql-action/analyze@v1 63 | -------------------------------------------------------------------------------- /tests/test_calculation.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | 5 | sys.path = [os.path.dirname(os.path.dirname(os.path.realpath(__file__)))] + sys.path 6 | from rockstarpy.transpile import Transpiler 7 | 8 | 9 | class TestCalculation(unittest.TestCase): 10 | def test_add_numbers(self): 11 | transpiler = Transpiler() 12 | py_line = transpiler.transpile_line("Put 1 plus 3 into my pocket\n") 13 | self.assertEqual(py_line, "my_pocket = 1 + 3\n") 14 | 15 | def test_add_poetic(self): 16 | transpiler = Transpiler() 17 | py_line = transpiler.transpile_line("Put this with that into my pocket\n") 18 | self.assertEqual(py_line, "my_pocket = this + that\n") 19 | 20 | def test_subtract_numbers(self): 21 | transpiler = Transpiler() 22 | py_line = transpiler.transpile_line("Put 1 minus 3 into my pocket\n") 23 | self.assertEqual(py_line, "my_pocket = 1 - 3\n") 24 | 25 | def test_subtract_poetic(self): 26 | transpiler = Transpiler() 27 | py_line = transpiler.transpile_line("Put this without that into my pocket\n") 28 | self.assertEqual(py_line, "my_pocket = this - that\n") 29 | 30 | def test_multiply_numbers(self): 31 | transpiler = Transpiler() 32 | py_line = transpiler.transpile_line("Put 1 times 3 into my pocket\n") 33 | self.assertEqual(py_line, "my_pocket = 1 * 3\n") 34 | 35 | def test_multiply_poetic(self): 36 | transpiler = Transpiler() 37 | py_line = transpiler.transpile_line("Put this of that into my pocket\n") 38 | self.assertEqual(py_line, "my_pocket = this * that\n") 39 | 40 | def test_divide_numbers(self): 41 | transpiler = Transpiler() 42 | py_line = transpiler.transpile_line("Put 1 over 3 into my pocket\n") 43 | self.assertEqual(py_line, "my_pocket = 1 / 3\n") 44 | 45 | def test_divide_poetic(self): 46 | transpiler = Transpiler() 47 | py_line = transpiler.transpile_line("Put this over that into my pocket\n") 48 | self.assertEqual(py_line, "my_pocket = this / that\n") 49 | 50 | def test_decrement(self): 51 | transpiler = Transpiler() 52 | py_line = transpiler.transpile_line("Knock the hate down\n") 53 | self.assertEqual(py_line, "the_hate -= 1\n") 54 | 55 | def test_increment(self): 56 | transpiler = Transpiler() 57 | py_line = transpiler.transpile_line("Build my money up\n") 58 | self.assertEqual(py_line, "my_money += 1\n") 59 | 60 | @unittest.skip("Compound assignment not implemented, yet") 61 | def test_compund_addition_assignment(self): 62 | transpiler = Transpiler() 63 | py_line = transpiler.transpile_line("Let X be with 10\n") 64 | self.assertEqual(py_line, "X += 10\n") 65 | 66 | 67 | if __name__ == "__main__": 68 | unittest.main() 69 | -------------------------------------------------------------------------------- /tests/test_conditionals.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | 5 | sys.path = [os.path.dirname(os.path.dirname(os.path.realpath(__file__)))] + sys.path 6 | from rockstarpy.transpile import Transpiler 7 | 8 | 9 | class TestConditionals(unittest.TestCase): 10 | def test_equals(self): 11 | transpiler = Transpiler() 12 | py_line = transpiler.transpile_line("If Tommy is nobody\n") 13 | self.assertEqual(py_line, "if Tommy == False:\n") 14 | 15 | def test_greater(self): 16 | transpiler = Transpiler() 17 | py_line = transpiler.transpile_line("If Tommy is stronger than Superman\n") 18 | self.assertEqual(py_line, "if Tommy > Superman:\n") 19 | 20 | def test_greater_equals(self): 21 | transpiler = Transpiler() 22 | py_line = transpiler.transpile_line("If Love is as high as mountain\n") 23 | self.assertEqual(py_line, "if Love >= mountain:\n") 24 | 25 | def test_less(self): 26 | transpiler = Transpiler() 27 | py_line = transpiler.transpile_line("If Tommy is weaker than a worm\n") 28 | self.assertEqual(py_line, "if Tommy < a_worm:\n") 29 | 30 | def test_less_equals(self): 31 | transpiler = Transpiler() 32 | py_line = transpiler.transpile_line("If my mind is as low as my soul\n") 33 | self.assertEqual(py_line, "if my_mind <= my_soul:\n") 34 | 35 | def test_while_equals(self): 36 | transpiler = Transpiler() 37 | py_line = transpiler.transpile_line("While Tommy is nobody\n") 38 | self.assertEqual(py_line, "while Tommy == False:\n") 39 | 40 | def test_until_greater(self): 41 | transpiler = Transpiler() 42 | py_line = transpiler.transpile_line("Until Tommy is stronger than Superman\n") 43 | self.assertEqual(py_line, "while not Tommy > Superman:\n") 44 | 45 | def test_not(self): 46 | transpiler = Transpiler() 47 | py_line = transpiler.transpile_line("If not number is 28\n") 48 | self.assertEqual(py_line, "if not number == 28:\n") 49 | 50 | def test_or(self): 51 | transpiler = Transpiler() 52 | py_line = transpiler.transpile_line("If Tommy is nobody or Billy is nobody\n") 53 | self.assertEqual(py_line, "if Tommy == False or Billy == False:\n") 54 | 55 | def test_and(self): 56 | transpiler = Transpiler() 57 | py_line = transpiler.transpile_line("If Tommy is nobody and Billy is nobody\n") 58 | self.assertEqual(py_line, "if Tommy == False and Billy == False:\n") 59 | 60 | @unittest.skip("apostrophe handling is not working fully") 61 | def test_aint(self): 62 | transpiler = Transpiler() 63 | py_line = transpiler.transpile_line("If Tommy ain't nobody\n") 64 | self.assertEqual(py_line, "if Tommy != False:\n") 65 | 66 | @unittest.skip("nor operator not implemented, yet") 67 | def test_nor(self): 68 | transpiler = Transpiler() 69 | py_line = transpiler.transpile_line("If Tommy is nobody nor Billy is nobody\n") 70 | self.assertEqual(py_line, "if not Tommy == False or Billy == False:\n") 71 | 72 | 73 | if __name__ == "__main__": 74 | 75 | unittest.main() 76 | -------------------------------------------------------------------------------- /tests/test_blocks.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | 5 | sys.path = [os.path.dirname(os.path.dirname(os.path.realpath(__file__)))] + sys.path 6 | from rockstarpy.transpile import Transpiler 7 | 8 | 9 | class TestBlocks(unittest.TestCase): 10 | def test_if_block(self): 11 | rock = ["If Tommy is nobody\n", 'Shout "Tommy?"\n', "\n", 'Shout "Tommy!"\n'] 12 | 13 | py = ["if Tommy == False:\n", ' print("Tommy?")\n', "", 'print("Tommy!")\n'] 14 | 15 | self.__run_block_test(rock, py) 16 | 17 | def test_if_else_block(self): 18 | rock = [ 19 | "If Tommy is nobody\n", 20 | 'Shout "Tommy?"\n', 21 | "Else\n", 22 | 'Shout "Tommy!"\n', 23 | "\n", 24 | 'Shout "Tommy!"\n', 25 | ] 26 | 27 | py = [ 28 | "if Tommy == False:\n", 29 | ' print("Tommy?")\n', 30 | "else:\n", 31 | ' print("Tommy!")\n', 32 | "", 33 | 'print("Tommy!")\n', 34 | ] 35 | 36 | self.__run_block_test(rock, py) 37 | 38 | def test_while_block(self): 39 | rock = [ 40 | "While Tommy is not 0\n", 41 | "Knock Tommy down\n", 42 | "\n", 43 | 'Shout "Tommy!"\n', 44 | ] 45 | 46 | py = ["while Tommy != 0:\n", " Tommy -= 1\n", "", 'print("Tommy!")\n'] 47 | 48 | self.__run_block_test(rock, py) 49 | 50 | def test_until_block(self): 51 | rock = ["Until Tommy is 0\n", "Knock Tommy down\n", "\n", 'Shout "Tommy!"\n'] 52 | 53 | py = ["while not Tommy == 0:\n", " Tommy -= 1\n", "", 'print("Tommy!")\n'] 54 | 55 | self.__run_block_test(rock, py) 56 | 57 | def test_function_numeric(self): 58 | rock = [ 59 | "Multiply takes Love and Life\n", 60 | "Give back Love of Life\n", 61 | "\n", 62 | "Say Multiply taking 3, 444\n", 63 | ] 64 | 65 | py = [ 66 | "def Multiply(Love, Life):\n", 67 | " return Love * Life\n", 68 | "", 69 | "print(Multiply(3, 444))\n", 70 | ] 71 | 72 | self.__run_block_test(rock, py) 73 | 74 | def test_function_variables(self): 75 | rock = [ 76 | "Multiply takes Love and Life\n", 77 | "Give back Love of Life\n", 78 | "\n", 79 | "Put 1 into my heart\n", 80 | "Put 666 into the devil\n", 81 | "Multiply taking my heart, the devil\n", 82 | ] 83 | 84 | py = [ 85 | "def Multiply(Love, Life):\n", 86 | " return Love * Life\n", 87 | "", 88 | "my_heart = 1\n", 89 | "the_devil = 666\n", 90 | "Multiply(my_heart, the_devil)\n", 91 | ] 92 | 93 | self.__run_block_test(rock, py) 94 | 95 | def __run_block_test(self, rock, py): 96 | transpiler = Transpiler() 97 | transpiled = [transpiler.transpile_line(line) for line in rock] 98 | self.assertEqual(len(py), len(transpiled)) 99 | for py_line, transpiled_line in zip(py, transpiled): 100 | self.assertEqual(py_line, transpiled_line) 101 | 102 | 103 | if __name__ == "__main__": 104 | unittest.main() 105 | -------------------------------------------------------------------------------- /tests/test_assignment.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | 5 | sys.path = [os.path.dirname(os.path.dirname(os.path.realpath(__file__)))] + sys.path 6 | from rockstarpy.transpile import Transpiler 7 | 8 | 9 | class TestAssignment(unittest.TestCase): 10 | def test_is_bool(self): 11 | transpiler = Transpiler() 12 | py_line = transpiler.transpile_line("Life is ok\n") 13 | self.assertEqual(py_line, "Life = True\n") 14 | 15 | def test_is_poetic(self): 16 | transpiler = Transpiler() 17 | py_line = transpiler.transpile_line("My life is a mess\n") 18 | self.assertEqual(py_line, "my_life = 14\n") 19 | 20 | py_line = transpiler.transpile_line("Life is a one day after another\n") 21 | self.assertEqual(py_line, "Life = 13357\n") 22 | 23 | def test_is_numeric(self): 24 | transpiler = Transpiler() 25 | py_line = transpiler.transpile_line("Seven is 11\n") 26 | self.assertEqual(py_line, "Seven = 11\n") 27 | 28 | def test_is_numeric_with_dot(self): 29 | transpiler = Transpiler() 30 | py_line = transpiler.transpile_line("X is 1.23\n") 31 | self.assertEqual(py_line, "X = 1.23\n") 32 | 33 | def test_is_poetic_with_dot(self): 34 | transpiler = Transpiler() 35 | py_line = transpiler.transpile_line( 36 | "My dreams were ice. A life unfulfilled; wakin' everybody up, taking booze and pills\n" 37 | ) 38 | self.assertEqual(py_line, "my_dreams = 3.1415926535\n") 39 | 40 | def test_is_poetic_with_hyphen(self): 41 | transpiler = Transpiler() 42 | py_line = transpiler.transpile_line("Greed is all-consuming\n") 43 | self.assertEqual(py_line, "Greed = 3\n") 44 | 45 | py_line = transpiler.transpile_line("God is power-hungry\n") 46 | self.assertEqual(py_line, "God = 2\n") 47 | 48 | def test_is_poetic_and_numeric(self): 49 | transpiler = Transpiler() 50 | py_line = transpiler.transpile_line("Car is 4 W D\n") 51 | self.assertEqual(py_line, "Car = 411\n") 52 | 53 | @unittest.skip("keywords are replaced too early in assignments") 54 | def test_is_poetic_keyword(self): 55 | transpiler = Transpiler() 56 | py_line = transpiler.transpile_line("Tommy was without\n") 57 | self.assertEqual(py_line, "Tommy = 7\n") 58 | 59 | def test_is_string(self): 60 | transpiler = Transpiler() 61 | py_line = transpiler.transpile_line('Message is "Hello"\n') 62 | self.assertEqual(py_line, 'Message = "Hello"\n') 63 | 64 | def test_let_be_bool(self): 65 | transpiler = Transpiler() 66 | py_line = transpiler.transpile_line("Let my beer be empty\n") 67 | self.assertEqual(py_line, "my_beer = False\n") 68 | 69 | def test_let_be_poetic(self): 70 | transpiler = Transpiler() 71 | py_line = transpiler.transpile_line("Let Stuart be a yellow Minion\n") 72 | self.assertEqual(py_line, "Stuart = 166\n") 73 | 74 | def test_let_be_numeric(self): 75 | transpiler = Transpiler() 76 | py_line = transpiler.transpile_line("Let One be 0\n") 77 | self.assertEqual(py_line, "One = 0\n") 78 | 79 | def test_let_be_string(self): 80 | transpiler = Transpiler() 81 | py_line = transpiler.transpile_line('Let the letter be "R"\n') 82 | self.assertEqual(py_line, 'the_letter = "R"\n') 83 | 84 | def test_put_into_bool(self): 85 | transpiler = Transpiler() 86 | py_line = transpiler.transpile_line("Put nothing into my hand\n") 87 | self.assertEqual(py_line, "my_hand = False\n") 88 | 89 | def test_put_into_poetic_assigns_variable(self): 90 | transpiler = Transpiler() 91 | py_line = transpiler.transpile_line("Put a flower into the vase\n") 92 | self.assertEqual(py_line, "the_vase = a_flower\n") 93 | 94 | def test_put_into_numeric(self): 95 | transpiler = Transpiler() 96 | py_line = transpiler.transpile_line("Put 3.14 into PI\n") 97 | self.assertEqual(py_line, "Pi = 3.14\n") 98 | 99 | def test_put_into_string(self): 100 | transpiler = Transpiler() 101 | py_line = transpiler.transpile_line('Put "letter" into the envelope\n') 102 | self.assertEqual(py_line, 'the_envelope = "letter"\n') 103 | 104 | 105 | if __name__ == "__main__": 106 | unittest.main() 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rockstar-py 2 | 3 |

Python transpiler for the esoteric language Rockstar

4 | 5 |

6 | Travis (.org) 7 | Codacy grade 8 | PyPI - Version 9 | PyPI - Status 10 | PyPI - Status 11 | PyPI - Python Version 12 | PyPI - Wheel 13 | GitHub top language 14 | Code Style - Black 15 | GitHub contributors 16 | GitHub stars 17 | GitHub issues 18 | GitHub code size in bytes 19 | GitHub Release Date 20 | GitHub commits since tagged version 21 | GitHub last commit 22 | License - MIT 23 |

24 | 25 | ## Getting Started 26 | 27 | These instructions will get you a copy of the project up and running on 28 | your local machine for development and testing purposes. 29 | 30 | ### Installing 31 | 32 | First, make sure you have installed a supported Python version (\>= 33 | 3.6). 34 | 35 | Now, the easiest way of installing **rockstar-py** is using pip: 36 | 37 | pip install rockstar-py 38 | 39 | (This may require `sudo` if you're installing globally on a \*nix 40 | system. 41 | 42 | You can also clone this project using git and install the package with 43 | setuptools: 44 | 45 | git clone https://github.com/yyyyyyyan/rockstar-py.git 46 | cd rockstar-py 47 | python3 setup.py install 48 | 49 | ## Usage 50 | 51 | If you installed the package using pip or setuptools, you can simply run rockstar-py in the command line: 52 | 53 | rockstar-py [-h] (-i INPUT | --stdin) [-o OUTPUT | --stdout | --exec] [-v] 54 | 55 | Otherwise, you can run the transpiler from inside the `rockstar-py` folder by running Python with the `rockstarpy` package: 56 | 57 | python3 rockstarpy [-h] (-i INPUT | --stdin) [-o OUTPUT | --stdout | --exec] [-v] 58 | 59 | Call `rockstar-py` with the flag `-h`/`--help` to see a description of all options: 60 | 61 | usage: rockstar-py [-h] (-i INPUT | --stdin) [-o OUTPUT | --stdout | --exec] [-v] 62 | 63 | Python transpiler for the esoteric language Rockstar 64 | 65 | optional arguments: 66 | -h, --help show this help message and exit 67 | -i INPUT, --input INPUT 68 | Input file (.rock) 69 | --stdin Stream in stdin 70 | -o OUTPUT, --output OUTPUT 71 | Output file (.py) 72 | --stdout Stream to stdout 73 | --exec Execute (without saving) the transpiled code 74 | -v, --version Version 75 | 76 | ## Examples 77 | 78 | Just to make it more clear, some examples with the 79 | [fizz.rock](https://github.com/yyyyyyyan/rockstar-py/blob/master/tests/fizz.rock) 80 | code. 81 | 82 | ### Basic usage 83 | 84 | > rockstar-py -i fizz.rock -o fizz.py 85 | > ls 86 | fizz.py fizz.rock 87 | > cat fizz.py 88 | def Midnight(your_heart, your_soul): 89 | while your_heart >= your_soul: # this is a comment 90 | your_heart = your_heart - your_soul 91 | return your_heart 92 | Desire = 100 93 | my_world = False 94 | Fire = 3 # i love comments 95 | Hate = 5 96 | while not my_world == Desire: 97 | my_world += 1 98 | if Midnight(my_world, Fire) == False and Midnight(my_world, Hate) == False: 99 | print("FizzBuzz!") 100 | continue 101 | if Midnight(my_world, Fire) == False: 102 | print("Fizz!") 103 | continue 104 | if Midnight(my_world, Hate) == False: 105 | print("Buzz!") 106 | continue 107 | print(my_world) 108 | 109 | ### Using `--stdout` 110 | 111 | > rockstar-py -i fizz.rock --stdout 112 | def Midnight(your_heart, your_soul): 113 | while your_heart >= your_soul: # this is a comment 114 | your_heart = your_heart - your_soul 115 | return your_heart 116 | Desire = 100 117 | my_world = False 118 | Fire = 3 # i love comments 119 | Hate = 5 120 | while not my_world == Desire: 121 | my_world += 1 122 | if Midnight(my_world, Fire) == False and Midnight(my_world, Hate) == False: 123 | print("FizzBuzz!") 124 | continue 125 | if Midnight(my_world, Fire) == False: 126 | print("Fizz!") 127 | continue 128 | if Midnight(my_world, Hate) == False: 129 | print("Buzz!") 130 | continue 131 | print(my_world) 132 | 133 | ### Using `--stdin` 134 | 135 | > rockstar-py --stdin -o fizz.py 136 | Midnight takes your heart and your soul 137 | While your heart is as high as your soul (this is a comment) 138 | Put your heart without your soul into your heart 139 | 140 | Give back your heart 141 | 142 | 143 | Desire's a lovestruck ladykiller 144 | My world is empty 145 | Fire's ice (i love comments) 146 | Hate is water 147 | Until my world is Desire, 148 | Build my world up 149 | If Midnight taking my world, Fire is nothing and Midnight taking my world, Hate is nothing 150 | Shout "FizzBuzz!" 151 | Take it to the top 152 | 153 | If Midnight taking my world, Fire is nothing 154 | Shout "Fizz!" 155 | Take it to the top 156 | 157 | If Midnight taking my world, Hate is nothing 158 | Say "Buzz!" 159 | Take it to the top 160 | 161 | Whisper my world 162 | [Ctrl+D] 163 | > ls 164 | fizz.py fizz.rock 165 | 166 | ### Using `--exec` 167 | 168 | > rockstar-py -i fizz.rock --exec 169 | 1 170 | 2 171 | Fizz! 172 | 4 173 | Buzz! 174 | Fizz! 175 | 7 176 | 8 177 | Fizz! 178 | Buzz! 179 | 11 180 | Fizz! 181 | 13 182 | 14 183 | FizzBuzz! 184 | 16 185 | 17 186 | Fizz! 187 | 19 188 | Buzz! 189 | Fizz! 190 | 22 191 | 23 192 | Fizz! 193 | Buzz! 194 | 26 195 | Fizz! 196 | 28 197 | 29 198 | FizzBuzz! 199 | 31 200 | 32 201 | Fizz! 202 | 34 203 | Buzz! 204 | Fizz! 205 | 37 206 | 38 207 | Fizz! 208 | Buzz! 209 | 41 210 | Fizz! 211 | 43 212 | 44 213 | FizzBuzz! 214 | 46 215 | 47 216 | Fizz! 217 | 49 218 | Buzz! 219 | Fizz! 220 | 52 221 | 53 222 | Fizz! 223 | Buzz! 224 | 56 225 | Fizz! 226 | 58 227 | 59 228 | FizzBuzz! 229 | 61 230 | 62 231 | Fizz! 232 | 64 233 | Buzz! 234 | Fizz! 235 | 67 236 | 68 237 | Fizz! 238 | Buzz! 239 | 71 240 | Fizz! 241 | 73 242 | 74 243 | FizzBuzz! 244 | 76 245 | 77 246 | Fizz! 247 | 79 248 | Buzz! 249 | Fizz! 250 | 82 251 | 83 252 | Fizz! 253 | Buzz! 254 | 86 255 | Fizz! 256 | 88 257 | 89 258 | FizzBuzz! 259 | 91 260 | 92 261 | Fizz! 262 | 94 263 | Buzz! 264 | Fizz! 265 | 97 266 | 98 267 | Fizz! 268 | Buzz! 269 | 270 | ## Contributing 271 | 272 | The project has basically reached its end, but I'm still accepting pull 273 | requests that improve speed and legibility of the code. 274 | 275 | ## Authors 276 | 277 | - **[yyyyyyyan](https://github.com/yyyyyyyan)** - *Initial work* 278 | 279 | ## Contributors 280 | 281 | Huge thanks to everyone who is contribuing to this project. Check them 282 | out at [Contributors](https://github.com/yyyyyyyan/rockstar-py/graphs/contributors)! 283 | 284 | ## License 285 | 286 | This project is licensed under the MIT License - see the 287 | [LICENSE](https://github.com/yyyyyyyan/rockstar-py/blob/master/LICENSE) 288 | file for details. 289 | 290 | ## Acknowledgments 291 | 292 | - Hat tip to [dylanbeattie](https://github.com/dylanbeattie/) for creating Rockstar 293 | - The FizzBuzz example works well. If valid code doesn’t work, create an issue so I can get a look. 294 | - I’ll work on the readibility and organization of the code, would love suggestions on how/where to do that. 295 | - I'd also love help with the tests. 296 | -------------------------------------------------------------------------------- /rockstarpy/transpile.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | class Transpiler(object): 5 | SIMPLE_VARIABLE_FMT = r"\b[A-Za-z]+\b" 6 | COMMON_VARIABLE_FMT = r"\b(?:[Aa]n?|[Tt]he|[Mm]y|[Yy]our) [a-z]+\b" 7 | PROPER_VARIABLE_FMT = r"\b[A-Z][A-Za-z]*(?: [A-Z][A-Za-z]*)*\b" 8 | REGEX_VARIABLES = r"(?:{}|{}|{})".format(COMMON_VARIABLE_FMT, PROPER_VARIABLE_FMT, SIMPLE_VARIABLE_FMT) 9 | QUOTE_STR_FMT = r"\"[^\"]*\"" 10 | 11 | def __init__(self): 12 | self.indentation_style = " " * 4 13 | self._current_indentation = 0 14 | self.in_function = False 15 | self.globals = set() 16 | self.most_recently_named = "" 17 | self.simple_subs = { 18 | "(": "#", 19 | ")": "", 20 | "Give back": "return", 21 | "Take it to the top": "continue", 22 | "Break it down": "break", 23 | " false ": " False ", 24 | " wrong ": " False ", 25 | " no ": " False ", 26 | " lies ": " False ", 27 | " null ": " False ", 28 | " nothing ": " False ", 29 | " nowhere ": " False ", 30 | " nobody ": " False ", 31 | " empty ": " False ", 32 | " gone ": " False ", 33 | " mysterious ": " False ", 34 | " true ": " True ", 35 | " right ": " True ", 36 | " yes ": " True ", 37 | " ok ": " True ", 38 | " plus ": " + ", 39 | " with ": " + ", 40 | " minus ": " - ", 41 | " without ": " - ", 42 | " times ": " * ", 43 | " of ": " * ", 44 | " over ": " / ", 45 | " is higher than ": " > ", 46 | " is greater than ": " > ", 47 | " is bigger than ": " > ", 48 | " is stronger than ": " > ", 49 | " is lower than ": " < ", 50 | " is less than ": " < ", 51 | " is smaller than ": " < ", 52 | " is weaker than ": " < ", 53 | " is as high as ": " >= ", 54 | " is as great as ": " >= ", 55 | " is as big as ": " >= ", 56 | " is as strong as ": " >= ", 57 | " is as low as ": " <= ", 58 | " is as little as ": " <= ", 59 | " is as small as ": " <= ", 60 | " is as weak as ": " <= ", 61 | " is not ": " != ", 62 | " aint ": " != ", 63 | "Until ": "while not ", 64 | "While ": "while ", 65 | } 66 | 67 | @property 68 | def current_indentation(self): 69 | return self._current_indentation 70 | 71 | @current_indentation.setter 72 | def current_indentation(self, value): 73 | self._current_indentation = value if value > 0 else 0 74 | 75 | def get_comments(self, line): 76 | comment_match = re.search(r"\((.*)\)", line) 77 | if comment_match: 78 | line = line.replace(comment_match.group(), "") 79 | comment = " # " + comment_match.group(1) 80 | elif "(" in line or ")" in line: 81 | raise SyntaxError("Missing parentheses in comment") 82 | else: 83 | comment = "" 84 | return line, comment 85 | 86 | def create_function(self, line): 87 | match = re.match( 88 | r"\b({0}) takes ({0}(?: and {0})*)\b".format(self.REGEX_VARIABLES), line 89 | ) 90 | if match: 91 | self.current_indentation += 1 92 | line = "def {}({}):".format( 93 | match.group(1), match.group(2).replace(" and", ",") 94 | ) 95 | self.in_function = True 96 | return line 97 | 98 | def create_while(self, line): 99 | if line.startswith("while "): 100 | line = line.replace(" is ", " == ") 101 | line += ":" 102 | self.current_indentation += 1 103 | return line 104 | 105 | def create_if(self, line): 106 | match = re.match(r"If .*", line) 107 | if match: 108 | self.current_indentation += 1 109 | line = line.replace(" is ", " == ") 110 | line = line.replace("If", "if") 111 | line += ":" 112 | return line 113 | 114 | def replace_let_be_with_is(self, line): 115 | match = re.match(r"Let ({0}) be (.+)".format(self.REGEX_VARIABLES), line) 116 | if match: 117 | return match.group(1) + " is " + match.group(2) 118 | return line 119 | 120 | def find_poetic_number_literal(self, line): 121 | poetic_type_literals_keywords = ["True", "False"] 122 | match = re.match( 123 | r"\b({0})(?: is|\'s| was| were) ([\d\w\.,\:\!\;\'\-\s]+)".format( 124 | self.REGEX_VARIABLES 125 | ), 126 | line, 127 | ) 128 | if match and match.group(2).split()[0] not in poetic_type_literals_keywords: 129 | line = "{} = ".format(match.group(1)) 130 | for word_number in match.group(2).split(): 131 | if re.match(r"\d+", word_number): 132 | line += str(word_number) 133 | else: 134 | period = "." if word_number.endswith(".") else "" 135 | alpha_word = re.sub(r"[^A-Za-z\-]", "", word_number) 136 | line += str(len(alpha_word) % 10) + period 137 | return line 138 | 139 | def find_variables(self, line, fmt, clean_func=str): 140 | variables = set(re.findall(fmt, line)) 141 | if variables: 142 | for variable in variables: 143 | line = re.sub(r"\b{}\b".format(variable), clean_func(variable).replace(" ", "_"), line) 144 | return line 145 | 146 | def find_proper_variables(self, line): 147 | return self.find_variables(line, self.PROPER_VARIABLE_FMT, lambda variable: variable.title()) 148 | 149 | def find_common_variables(self, line): 150 | return self.find_variables(line, self.COMMON_VARIABLE_FMT, lambda variable: variable.lower()) 151 | 152 | def find_named(self, line): 153 | match = re.match(r"([A-Za-z]+(?:_[A-Za-z]+)*) [+-]?= .+", line) 154 | if match: 155 | return match.group(1) 156 | 157 | def get_strings(self, line): 158 | strings = dict() 159 | says_match = re.match(r"({}) says (.*)".format(self.REGEX_VARIABLES), line) 160 | if says_match: 161 | line = says_match.group(1) + ' = {str_0}' 162 | strings["str_0"] = '"{}"'.format(says_match.group(2).replace('"', r'\"')) 163 | return line, strings 164 | else: 165 | for str_number, string in enumerate(re.findall(self.QUOTE_STR_FMT, line)): 166 | fmt_var = f"str_{str_number}" 167 | line = re.sub(self.QUOTE_STR_FMT, f"{{str_{str_number}}}", line, 1) 168 | strings[fmt_var] = string 169 | return line, strings 170 | 171 | def transpile_line(self, line): 172 | if line == "\n": 173 | self.current_indentation -= 1 174 | return "" 175 | else: 176 | line_ident = self.indentation_style * self.current_indentation 177 | self.in_function = False if self.current_indentation == 0 else self.in_function 178 | 179 | py_line, line_strings = self.get_strings(line) 180 | py_line, comments = self.get_comments(py_line) 181 | 182 | for key in self.simple_subs: 183 | py_line = py_line.strip() 184 | py_line += " " 185 | py_line = py_line.replace(key, self.simple_subs[key]) 186 | py_line = py_line.strip("\n ,.;") 187 | 188 | py_line = self.replace_let_be_with_is(py_line) 189 | py_line = self.find_poetic_number_literal(py_line) 190 | 191 | py_line = py_line.replace("'", "") 192 | 193 | for key in self.simple_subs: 194 | py_line = py_line.strip() 195 | py_line += " " 196 | py_line = py_line.replace(key, self.simple_subs[key]) 197 | py_line = py_line.strip("\n ,.;") 198 | 199 | most_recently_named_keywords = [ 200 | " it ", 201 | " he ", 202 | " she ", 203 | " him ", 204 | " her ", 205 | " them ", 206 | " they ", 207 | " ze ", 208 | " hir ", 209 | " zie ", 210 | " zir ", 211 | " xe ", 212 | " xem ", 213 | " ve ", 214 | " ver ", 215 | ] 216 | for keyword in most_recently_named_keywords: 217 | py_line = py_line.replace( 218 | keyword, " {} ".format(self.most_recently_named) 219 | ) 220 | 221 | py_line = self.create_function(py_line) 222 | py_line = self.create_while(py_line) 223 | py_line = self.create_if(py_line) 224 | line_ident = self.indentation_style * (self.current_indentation - 1) if py_line == "Else" else line_ident 225 | py_line = "else:" if py_line == "Else" else py_line 226 | 227 | py_line = re.sub( 228 | r"Put (.*) into ({})".format(self.REGEX_VARIABLES), 229 | r"\g<2> = \g<1>", 230 | py_line, 231 | ) 232 | py_line = re.sub( 233 | r"Build ({}) up".format(self.REGEX_VARIABLES), r"\g<1> += 1", py_line 234 | ) 235 | py_line = re.sub( 236 | r"Knock ({}) down(\, down)*".format(self.REGEX_VARIABLES), 237 | r"\g<1> -= " + str(1 + py_line.count(", down")), 238 | py_line, 239 | ) 240 | py_line = re.sub( 241 | r"Listen to ({})".format(self.REGEX_VARIABLES), 242 | r"\g<1> = input()", 243 | py_line, 244 | ) 245 | py_line = re.sub( 246 | r"(?:Say|Shout|Whisper|Scream) (.*)", r"print(\g<1>)", py_line 247 | ) 248 | 249 | py_line = py_line.replace(" is ", " = ", 1) 250 | 251 | py_line = re.sub( 252 | r"({0}) taking ((?:{0}|\"[^\"]*\"|[0-9]+)(?:, ?(?:{0}|\"[^\"]*\"|[0-9]+))*)".format( 253 | self.REGEX_VARIABLES 254 | ), 255 | r"\g<1>(\g<2>)", 256 | py_line, 257 | ) 258 | 259 | py_line = self.find_proper_variables(py_line) 260 | py_line = self.find_common_variables(py_line) 261 | 262 | line_named = self.find_named(py_line) 263 | if line_named: 264 | self.most_recently_named = line_named 265 | if not self.in_function: 266 | self.globals.add(line_named) 267 | elif line_named in self.globals: 268 | py_line = f"global {line_named}\n" + line_ident + py_line 269 | 270 | py_line = py_line.format(**line_strings) 271 | 272 | return line_ident + py_line + comments + "\n" 273 | --------------------------------------------------------------------------------