├── .gitignore ├── LICENSE ├── README.md ├── v2 ├── README.md └── src │ ├── hello_world.py │ ├── import.py │ ├── keywords │ └── __init__.py │ ├── punctuation │ └── __init__.py │ ├── quine.py │ ├── quine_golfed.py │ └── util │ └── __init__.py └── v4 ├── README.md └── src ├── classes.py ├── expressions.py ├── hello_world.py ├── import.py ├── import └── __init__.py ├── quine.py └── util └── __init__.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # JetBrains 132 | .idea/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Run-on Python 2 | 3 | ## Minimizing Punctuation in Python 4 | 5 | There are lots of punctuation marks that can appear in Python code. Strictly speaking, *any* punctuation mark can appear inside a string literal, but that's not really part of Python's *syntax*, as such marks won't actually mean anything to the interpreter[^1]. 6 | 7 | [^1]: I'm well aware of f-strings and its predecessors, but those are once again *syntactic* elements of the language. 8 | 9 | Thus, this project considers all *single* punctuation marks that are syntactically valid *outside* of string literals. As of release 3.11[^2][^3], they are 10 | 11 | `! " # % & ' () * + , - . / : ; < = > @ [] \ ^ {} | ~` 12 | 13 | Compositions of two or three of the above symbols, like `+=` or `...`, are just that, compositions, and are not taken to be distinct marks. However, we will have to ensure that all valid compositions are also unnecessary. 14 | 15 | [^2]: If [PEP-645](https://peps.python.org/pep-0645/) is accepted, which it rightly should, then `?` would enter the mix. 16 | [^3]: `_` is not a punctuation mark, but rather an identifier (and thus more or less a letter). Though `string` thinks it *is* punctuation... 17 | 18 | So the question is: how many of the above 28 punctuation marks are *necessary* to write any and all Python code? 19 | 20 | As far as I can reason, the answer is ***four***: `()`, `:`, and `*`, a measly ***two***: `@` and `:`, or just ***one***: `:`, depending on the liberties you take along the way. Let's go over some of those. 21 | 22 | ## Our Mission Statement 23 | 24 | ### To `exec` or Not To `exec` 25 | 26 | Let's make it clear exactly what the goal is here. When I first started this project, I knew I had to avoid `exec` and its cousins at all costs; it was already the key to minimizing the space of *all* characters to execute arbitrary code, as shown [here](https://codegolf.stackexchange.com/questions/110648/fewest-distinct-characters-for-turing-completeness). 27 | 28 | This is extraordinarily boring, as wrapping all your code inside a string literal (and a hard-to-read one at that!) almost defeats the point, as one could simply write code as they always do and read the file into an encoder. There's no meat to the problem, not to mention that such code is unparseable by your favorite IDE. So, we weren't allowed to wrap everything up in a string and call it a day. 29 | 30 | *However*, as I experimented with the use of decorators to forego explicit function calls, I realized that `exec` could actually save the day. See, the normal way to use `exec` to hack your way to universality requires *three* punctuation marks, `()` and `+`, to chain `chr` calls together, obtaining any character you need using only digits. 31 | 32 | As I discovered, though, we can bring ourselves down to only two, `@` and `:`, to build those kinds of constructions. Now, this is where the judgment call comes in: using the three punctuation marks needed to normally use `exec` in a minimal way is, frankly, boring. Meanwhile, working down to two is, in my opinion, much trickier and fun. Thus, I decided to bring `exec` back for the show, to finally attain the true minimum of Python punctuationlessness. 33 | 34 | So, a rift was formed: `exec` was banned on one side, and glorified on the other. If you want to check out the original machinations, Run-on Python v4, head to `v4/`; there you will find the original text of `README.md`, which explains how to weasel your way into punctuation nirvana *without* `exec`. If you'd like to make Guido cry even more, and see how just two will do, you can check out Run-on Python v2 in `v2/`, which has an accompanying `README.md`. 35 | 36 | Both directories also include helper code that can be imported for use in other programs (see `util/`). This helper code includes a number of useful constructs for the respective Run-on Python version, written entirely within their own constraints. 37 | 38 | ### Some Common Bonds 39 | 40 | Despite this rift, there are still some common principles underlying all of Run-on Python's code. Most important of them is the ability to emulate *any* possible Python syntax or construction. This doesn't mean that *everything* will be possible (most things certainly won't be), but that *anything* can be approximated well enough in form and function. For example, Run-on Python v4 dives into how to ditch the `=` sign in code, replacing it with `__eq__` calls and `__setitem__` on the dictionary of variables. These substitutions match the function of `=` (and `==`) *exactly*, at the interpreter level. 41 | 42 | But what about default function arguments? You'd say there's no way to emulate those with some funky magic method, and you'd be correct. However, we can still *approximate* the form and function of default arguments by explicitly passing `None` in our calls (which is what the interpreter is doing under the hood!). We can then write our functions like 43 | ```python 44 | def foo(bar, baz): 45 | print(bar) 46 | print(baz if baz is not None else "Default argument!") 47 | ``` 48 | 49 | This is just one small example of what Run-on Python is trying to do: let you write whatever code you want, but your keyboard is broken in a very specific way that doesn't let you type punctuation, and thus force you to take some creative liberties in getting that code to behave the same way. 50 | 51 | Of course, "behave the same way" is a bit subjective sometimes, as one could claim that a Turing machine can "behave" identically to any Python program. You'd be right, but you'd also be very wrong: a Turing machine has no variables, nor classes, nor functions, nor printing to `stdout`. Being able to compute anything is not quite enough. 52 | 53 | Basically, one should be able to take any existing Python code, make some (probably several) adjustments, and obtain Run-on Python code (v2 or v4) that does the same thing in the same ways. Capiche? Cool, let's do it. 54 | 55 | ## Wait, Didn't You Mention Using Just One? 56 | 57 | I did. Recently, some of the lovely competitors on [code.golf](https://code.golf/) were intrigued by this project's premise. Within 24 hours, golfer extraordinaire @Lydxn devised the following contraption to perform arbitrary string execution with just one punctuation mark, `:`, and a single copy of it at that: 58 | ```python 59 | from builtins import eval as __path__ 60 | from binascii import unhexlify as __getattr__ 61 | from __main__ import efbbbf7072696e74282248656c6c6f2c20776f726c64212229 as code 62 | class __getattr__: 63 | from builtins import exec as __contains__ 64 | from random import seed as __init__ 65 | from __main__ import _ as C 66 | code in C 67 | ``` 68 | 69 | This code is a work of art, everything I could ever hope `v1` to be and more, but I nonetheless hesitate slightly to consider it the end-all of this project's endeavors. 70 | 71 | Firstly, you may notice that the code of interest is stored as a hex string in an identifier; we'll employ this trick in `v2` to save some time and headaches, but the idea of it being strictly _necessary_ is not quite my cup of tea. 72 | 73 | Secondly, two external-but-builtin packages are utilized to "run" the compressed code. The way they are used, and the exploitation of the `import` system in general, is a stroke of genius, but reliance on pure Python code which does _not_ conform to our rules is troubling. We'll see that `v2` itself requires the `operator` package, though, so plenty of slack should be cut in this regard; then again, `v4` needs none of this fiddling. 74 | 75 | But at the end of the day, I love this code. And I love `v4`, and I love `v2`, and you should too. Something about journeys and destinations, and something else about the good, the bad, and the ugly. 76 | 77 | ## Some Other Notes 78 | 79 | This challenge came to me in an afternoon, partly inspired by [pyfuck](https://github.com/wanqizhu/pyfuck). There's no good reason to ever write code this way, but it's kinda funny. Shoutout to @commandblockguy, @iPhoenix, and @LogicalJoe for comments and discussion on this monstrosity as it developed. 80 | 81 | I'll update the utilities whenever I feel like it, and thus they may not exactly conform to any details I happen to give in the corresponding READMEs. Let it be an exercise for the coder to deduce exactly what everything is doing. 82 | 83 | In particular, if I haven't gotten around to it yet, stuff may be broken because classes contain more magic methods by default than they used to. Adding (or subtracting, if you're downgrading your Python version) one or two to the index being fetched in the attribute fetchers should resolve things. 84 | -------------------------------------------------------------------------------- /v2/README.md: -------------------------------------------------------------------------------- 1 | # Run-on Python v2 2 | 3 | ## Decorator? I Hardly Know Her! 4 | If you read the main `README.md` and are at least decently familiar with Python, you should be able to guess exactly how we're gonna accomplish our goals. If not, though, a quick refresher. 5 | 6 | ### Oh Honey Honey 7 | A *decorator* is usually described as a device that modifies some existing function. The syntax is `@[name]`, which appears just above a function definition. For example, in the following class, we use `@property` to turn a standard getter method into one that doesn't need to be called: 8 | ```python 9 | class Foo: 10 | def my_value(self): 11 | return 7 12 | 13 | @property 14 | def my_property(self): 15 | return 7 16 | 17 | print(Foo().my_value()) 18 | print(Foo().my_property) 19 | ``` 20 | 21 | However,the key fact about decorators is that they are just sugar; namely, decorator syntax is precisely equivalent to the following: 22 | ```python 23 | my_property = property(my_property) 24 | ``` 25 | (Kudos if you knew that `property` was a built-in function) 26 | 27 | Furthermore, the decorator is allowed to be *anything* callable; it doesn't need to return a first-class function. For example, 28 | ```python 29 | @str 30 | def my_func(): 31 | print("Off I go!") 32 | 33 | assert isinstance(my_func, str) 34 | ``` 35 | 36 | Perhaps this is enough to predict where we are headed. If not, then a final reminder: decorators can be chained. 37 | ```python 38 | @list 39 | @str 40 | def my_func(): 41 | print("Off I go!") 42 | 43 | assert isinstance(my_func, list) 44 | ``` 45 | This will permit us to do some very abhorrent things to our functions; in particular, we can compose functions without making explicit calls, granting us nearly the full power of functional programming. 46 | 47 | ### One Is The Lonelist Number 48 | Despite their utility, decorators have one absolutely devastating flaw: they only take one argument, no more, no less. This eliminates *most* built-ins from our workspace, including non-unary operators. How can we continue? 49 | 50 | Well, you first may cry, "But I've seen decorators take arguments!" Indeed you probably have, but not in the way that you think, and this will kill our efforts to use them for two reasons. Decorators do support the syntax `@[name]([args])`, but pay attention to how this is actually defined. Consider a decorator which repeats a function some number of times before returning: 51 | ```python 52 | @repeat(times=3) 53 | def divide_by_8(x): 54 | return x // 2 55 | 56 | assert divide_by_8(72) == 9 57 | ``` 58 | But how do we *define* `repeat`? Recall that decorators (usually) take a function as an argument and return another function: 59 | ```python 60 | def repeat(times): 61 | def inner(func): 62 | def repeater(x): 63 | for _ in range(times): 64 | x = func(x) 65 | 66 | return x 67 | 68 | return repeater 69 | 70 | return inner 71 | ``` 72 | Just look at that nesting! Very very few built-ins do anything close to this, so we'd have to define such a decorator ourselves. But remember, we're trying to get rid of `()`! We can't possibly define our own decorators[^1], let alone call a decorator with arguments in the first place. 73 | 74 | [^1]: To anybody who just thought about `lambda`s, pat yourself on the back, but also see if you can define a decorator that actually does something useful. 75 | 76 | ### An Executive Order 77 | With a heavy heart, I posit that we need `exec` to continue; it's the only way to call *n*-ary functions, despite our having to build them into a string to do so. But we should not get so hung up on this lamentable inclusion, for by its hand our journey to punctuation nirvana can continue. And besides, we don't have to worry too much about it until the end; we just need to focus on building arbitrary strings, which is still plenty of fun. 78 | 79 | ## Hack and Slash 80 | 81 | Unsurprisingly, the easiest way to build arbitrary strings is to use `@str`. Specifically, we'll make use of the fact that every object in Python has a string representation obtainable from `str`[^2]. Our strategy will then be to acquire various string methods to slice up and connect these strings, then `exec` the whole thing. Let's get crackin'. 82 | 83 | [^2]: *Technically*, this is handled by `repr`, which is in fact what *should* be used in the case of objects that override `__str__` for reasonable purposes. Our classes, however, don't, so we'll stick with the shorter call. 84 | 85 | ### Staying Classy 86 | For objects that don't have a canonical string representation (e.g. functions), Python makes do with something like this: 87 | ```python 88 | assert str(my_func) == "" 89 | ``` 90 | where `__main__` is simply whatever module contains the function (in this case, the top-level one). This is all well and good, but some of you have probably been screaming your heads off for the past few paragraphs: function definitions require parentheses! Fear not, for decorators can decorate another, friendlier set of objects: classes. Classes come with the bonus of not requiring any parentheses to define, as an empty class is simply 91 | ```python 92 | class Foo: 93 | pass 94 | ``` 95 | It's string representation is similar: 96 | ```python 97 | assert str(Foo) == "" 98 | ``` 99 | 100 | The bottom line is that we've got our class name in the string representation, but it's surrounded by a bunch of other fluff that we'll need to excise. We'll also need to deal with punctuation marks, as those by definition cannot appear in identifiers. The latter, however, is not too hard using `chr` and some clever little `lambda` constructs that became available in 3.9: 101 | ```python 102 | @chr 103 | @lambda _: 32 104 | class ThisIsASpace: 105 | pass 106 | 107 | assert ThisIsASpace == " " 108 | ``` 109 | 110 | In fact, this dastardly device can implement `=` all by itself! 111 | ```python 112 | @lambda _: OtherThing 113 | class MyThing: 114 | pass 115 | 116 | assert MyThing == OtherThing 117 | ``` 118 | 119 | ### Operator? I Hardly... 120 | Like a surgeon diving delicately into the intricate innards of their patient, we will endeavor to `slice` out the good parts of our strings from the bad. Indeed, we can accomplish this since `slice` has multiple arities, and in particular 121 | ```python 122 | x[:i] == x.__getitem__(slice(None, i, None)) == x.__getitem__(slice(i)) 123 | ``` 124 | 125 | Thus, we can obtain slices from the left, but what about the `__getitem__` call? Well, this is where the unassuming `operator` module enters the scene, armed with two functions that saved this project before it even began: `attrgetter` and `itemgetter`. These beauties were devised for their utility in calls to `map` and its other functional cousins, and are equivalent to 126 | ```python 127 | assert attrgetter("x")(y) == y.x 128 | assert itemgetter(x)(y) == y[x] 129 | ``` 130 | Just look at that currying! I claimed before that *most* built-ins do not admit this kind of design, but these are among the exceptions. They thus allow us to make the following construction: 131 | ```python 132 | @itemgetter 133 | @lambda _: 0 134 | class GetTheFirstItem: 135 | pass 136 | 137 | @GetTheFirstItem 138 | @str 139 | class ThisIsALeftAngleBracket: 140 | pass 141 | 142 | assert ThisIsALeftAngleBracket == "<" 143 | ``` 144 | And this is just the tip of the iceberg! With `@slice`, we can grab a whole section of the string as desired... except, we can't, at least not in the way we want. Recall that the actual string we want is at the *end* of the representation, and might be preceded by all kinds of gibberish depending on the module. But, if we can slice from the end instead, the amount we have to cut is fixed, namely `'>` at the very end. 145 | 146 | We can do this via `reversed`, and then leverage the fact that slicing works just as well on lists: 147 | ```python 148 | @itemgetter 149 | @slice 150 | @lambda _: 23 151 | class Slice23: 152 | pass 153 | 154 | @itemgetter 155 | @slice 156 | @lambda _: 25 157 | class Slice25: 158 | pass 159 | 160 | @Slice23 161 | @list 162 | @reversed 163 | @Slice25 164 | @list 165 | @reversed 166 | @str 167 | class ThisIsAStringOfLength23: 168 | pass 169 | ``` 170 | But wait, we don't get a string out in the end! It's just a darn list of characters! And you know what that means... 171 | 172 | ## `/attribute` 173 | This part's pretty easy to piece together. We've already got all the ingredients: `attrgetter`, `itemgetter`, and a little ol' friend called `dir`. We can mix these together into a delicious attribute-accessing parfait, complete with all of your favorite string methods and functions. 174 | 175 | Let's get a hold of `str.__add__` first, since it's pretty easy: 176 | ```python 177 | @attrgetter 178 | @next 179 | @iter 180 | @dir 181 | @lambda _: str 182 | class StrDotAdd: 183 | pass 184 | ``` 185 | We use it by attaching it to one string (i.e. decorating the class with the first operand), then calling the attachment on the other. Recall our use of carefully-chosen `slice`s: 186 | ```python 187 | @StrDotAdd 188 | @Slice4 189 | @list 190 | @reversed 191 | @Slice6 192 | @list 193 | @reversed 194 | @str 195 | class Left: 196 | pass 197 | 198 | @Left 199 | @Slice5 200 | @list 201 | @reversed 202 | @Slice7 203 | @list 204 | @reversed 205 | @str 206 | class Right: 207 | pass 208 | 209 | assert Right == "LeftRight" 210 | ``` 211 | 212 | ### Joining the Cause 213 | 214 | Our good friend `str.join` is a bit stranger; we need to first bind it to the empty string so we can join up our lists of characters with no separators. We'll get a hold of the empty string using another clever slice: 215 | ```python 216 | @Slice0 217 | @str 218 | class EmptyString: 219 | pass 220 | ``` 221 | 222 | Now we need `join` itself, which lives at index 56 inside `dir(str)`: 223 | ```python 224 | @itemgetter 225 | @lambda _: 56 226 | class GetItem56: 227 | pass 228 | 229 | @attrgetter 230 | @GetItem56 231 | @dir 232 | @lambda _: str 233 | class StrDotJoin: 234 | pass 235 | 236 | @StrDotJoin 237 | @Slice0 238 | @str 239 | class EmptyDotJoin: 240 | pass 241 | ``` 242 | And voila! We can reconnect our lists of characters from earlier, and thus revel in our ability to construct arbitrary strings. Just add up the parts, using `chr` where necessary to nab punctuation, and we're done! 243 | 244 | ### We Need To Go Deeper 245 | 246 | Armed with crude but arbitrary string execution, we can bootstrap our way to more powerful constructs. Each of these steps is executed for-realsies in `v2/src/util`, wherein you can import any you like to build your barely-readable code. And while I may have a penchant for the simplistic model of decorator chaining == string concatenation, any substantial code simply deserves better. 247 | 248 | First, we'll pick up some methods to turn hex strings into not-hex strings: 249 | ```python 250 | @attrgetter 251 | @GetItem43 252 | @dir 253 | @lambda _: bytes 254 | class _FromHex: 255 | pass 256 | 257 | @_FromHex 258 | @lambda _: bytes 259 | class FromHex: 260 | pass 261 | 262 | @attrgetter 263 | @GetItem39 264 | @dir 265 | @lambda _: bytes 266 | class _Decode: 267 | pass 268 | 269 | @_Decode 270 | @lambda _: bytes 271 | class Decode: 272 | pass 273 | ``` 274 | 275 | Next, we'll make a helper function to evaluate a hex string derived from an integer literal (in hex, so it at least _might_ fit on the screen). This helper, naturally, calls what will become itself on itself: 276 | ```python 277 | @iter 278 | @hex 279 | @lambda _: 0x6c616d626461205f3a6576616c2862797465732e66726f6d6865782866227b5f3a787d22292e6465636f6465282929 280 | class _EvalHex: 281 | pass 282 | 283 | @eval 284 | @Decode 285 | @FromHex 286 | @Join 287 | @list 288 | @lambda _: _EvalHex 289 | @next 290 | @lambda _: _EvalHex 291 | @next 292 | @lambda _: _EvalHex 293 | class EvalHex: 294 | pass 295 | ``` 296 | 297 | The assembly is subtle: we use `next` twice to skip the leading `0x` from `hex()`, then consume the rest of the iterator into a `list`, as `str(iter(foo))` is the string representation of `iter(foo)` (which isn't very useful). 298 | 299 | Now we can `EvalHex` whatever we'd like to execute worse and worse code. While we'd like to avoid using it too much, lest our programs become truly unreadable, but it does make our next trick much easier: matrix multiplication. 300 | 301 | ### Seriously, Why Is This Here? 302 | 303 | In the distant past of 2014, [it was decided](https://peps.python.org/pep-0465/) that Python should have a matrix multiplication operator. In a stroke a punning, its symbol is `@`, which proves unreasonably convenient for the task at hand. By defining classes which implement `__matmul__`, we can perform _binary_ operations without any extra punctuation _or_ resorting to hex strings! 304 | 305 | The general approach will be to use `@lambda _:` decorators, as they can have arbitrary expressions as bodies. We'll start each expression with the class whose operator we'd like to use, followed by a list of `@` operations. The class will collect the results of the operations, reducing left-associatively so that the `__matmul__` calls always succeed. For example, the `Args` class collects each subsequent element into a list: 306 | ```python 307 | @lambda _: Args @ 1 @ 2 @ 3 308 | class Example: 309 | pass 310 | ``` 311 | 312 | If we call `Example` on an argument, it will splat those elements into a function call of that argument: 313 | ```python 314 | @Example 315 | @lambda _: print 316 | class Function: 317 | pass 318 | ``` 319 | 320 | The `util` package contains many more fantastic Matthew Mullers; their exact usage and syntax is left as an exercise to the programmer. You can also find other shortcuts like `GetName`, which takes the headache out of reverse-slicing your way to the name of a class. All of this amounts to a surprising breadth of possible paradigms for writing Run-on Python, so please do run and explore with wild abandon. 321 | 322 | ## How Can I Write Run-on Python? 323 | 324 | In contrast to `v4`, a transpiler for `v2` code is actually decently doable. Only some very basic components are required in any given program, with the rest easy to generate on-the-fly by simply reading any given program as a string. Some care would need to be taken for imports, proper namespacing, and generally `exec` tomfoolery, but that can be pushed to the user. 325 | 326 | If such a transpiler ever comes to exist, it will appear here. Otherwise, enjoy writing Run-on Python by-hand and utilizing your precious moments on this Earth to create something truly ridiculous. -------------------------------------------------------------------------------- /v2/src/hello_world.py: -------------------------------------------------------------------------------- 1 | from util import Concat 2 | from util import EmptyString 3 | from util import GetName 4 | 5 | from punctuation import Space 6 | from punctuation import ExclamationMark 7 | 8 | 9 | @Concat 10 | @GetName 11 | class Hello: 12 | pass 13 | 14 | 15 | @Concat 16 | @GetName 17 | class World: 18 | pass 19 | 20 | 21 | @print 22 | @Hello 23 | @Space 24 | @World 25 | @ExclamationMark 26 | @lambda _: EmptyString 27 | class Main: 28 | pass 29 | -------------------------------------------------------------------------------- /v2/src/import.py: -------------------------------------------------------------------------------- 1 | from keywords import From 2 | from keywords import Import 3 | 4 | from punctuation import Asterisk 5 | from punctuation import Space 6 | 7 | from util import Casefold 8 | from util import Concat 9 | from util import EmptyString 10 | from util import GetName 11 | 12 | 13 | @Concat 14 | @Casefold 15 | @GetName 16 | class Math: 17 | pass 18 | 19 | 20 | @exec 21 | @From 22 | @Space 23 | @Math 24 | @Space 25 | @Import 26 | @Asterisk 27 | @lambda _: EmptyString 28 | class FromMathImportAll: 29 | pass 30 | -------------------------------------------------------------------------------- /v2/src/keywords/__init__.py: -------------------------------------------------------------------------------- 1 | from operator import itemgetter 2 | 3 | from util import Args 4 | from util import Call 5 | from util import Casefold 6 | from util import Compose 7 | from util import Concat 8 | from util import GetName 9 | from util import _1 10 | 11 | 12 | @lambda _: Compose @ GetName @ Casefold @ Concat 13 | class _GetName: 14 | pass 15 | 16 | 17 | @_GetName 18 | class And: 19 | pass 20 | 21 | 22 | @_GetName 23 | class As: 24 | pass 25 | 26 | 27 | @_GetName 28 | class Assert: 29 | pass 30 | 31 | 32 | @_GetName 33 | class Async: 34 | pass 35 | 36 | 37 | @_GetName 38 | class Await: 39 | pass 40 | 41 | 42 | @_GetName 43 | class Break: 44 | pass 45 | 46 | 47 | @_GetName 48 | class Class: 49 | pass 50 | 51 | 52 | @_GetName 53 | class Continue: 54 | pass 55 | 56 | 57 | @_GetName 58 | class Def: 59 | pass 60 | 61 | 62 | @_GetName 63 | class Del: 64 | pass 65 | 66 | 67 | @_GetName 68 | class Elif: 69 | pass 70 | 71 | 72 | @_GetName 73 | class Else: 74 | pass 75 | 76 | 77 | @_GetName 78 | class Except: 79 | pass 80 | 81 | 82 | @_GetName 83 | class Finally: 84 | pass 85 | 86 | 87 | @_GetName 88 | class For: 89 | pass 90 | 91 | 92 | @_GetName 93 | class From: 94 | pass 95 | 96 | 97 | @_GetName 98 | class Global: 99 | pass 100 | 101 | 102 | @_GetName 103 | class If: 104 | pass 105 | 106 | 107 | @_GetName 108 | class Import: 109 | pass 110 | 111 | 112 | @_GetName 113 | class In: 114 | pass 115 | 116 | 117 | @_GetName 118 | class Is: 119 | pass 120 | 121 | 122 | @_GetName 123 | class Lambda: 124 | pass 125 | 126 | 127 | @_GetName 128 | class Nonlocal: 129 | pass 130 | 131 | 132 | @_GetName 133 | class Not: 134 | pass 135 | 136 | 137 | @_GetName 138 | class Or: 139 | pass 140 | 141 | 142 | @_GetName 143 | class Pass: 144 | pass 145 | 146 | 147 | @_GetName 148 | class Raise: 149 | pass 150 | 151 | 152 | @_GetName 153 | class Return: 154 | pass 155 | 156 | 157 | @_GetName 158 | class Try: 159 | pass 160 | 161 | 162 | @_GetName 163 | class While: 164 | pass 165 | 166 | 167 | @_GetName 168 | class With: 169 | pass 170 | 171 | 172 | @_GetName 173 | class Yield: 174 | pass 175 | 176 | 177 | @itemgetter 178 | @slice 179 | @lambda _: _1 180 | class _Trim: 181 | pass 182 | 183 | 184 | @lambda _: Compose @ GetName @ _Trim @ Concat 185 | class _GetSingleton: 186 | pass 187 | 188 | 189 | @_GetSingleton 190 | class False_: 191 | pass 192 | 193 | 194 | @_GetSingleton 195 | class None_: 196 | pass 197 | 198 | 199 | @_GetSingleton 200 | class True_: 201 | pass 202 | -------------------------------------------------------------------------------- /v2/src/punctuation/__init__.py: -------------------------------------------------------------------------------- 1 | from operator import attrgetter 2 | 3 | 4 | @attrgetter 5 | @next 6 | @iter 7 | @dir 8 | @lambda _: str 9 | class Concat: 10 | pass 11 | 12 | 13 | @Concat 14 | @chr 15 | @lambda _: 9 16 | class Tab: 17 | pass 18 | 19 | 20 | @Concat 21 | @chr 22 | @lambda _: 10 23 | class Newline: 24 | pass 25 | 26 | 27 | @Concat 28 | @chr 29 | @lambda _: 32 30 | class Space: 31 | pass 32 | 33 | 34 | @Concat 35 | @chr 36 | @lambda _: 33 37 | class ExclamationMark: 38 | pass 39 | 40 | 41 | @Concat 42 | @chr 43 | @lambda _: 34 44 | class QuotationMark: 45 | pass 46 | 47 | 48 | @Concat 49 | @chr 50 | @lambda _: 35 51 | class NumberSign: 52 | pass 53 | 54 | 55 | @Concat 56 | @chr 57 | @lambda _: 36 58 | class DollarSign: 59 | pass 60 | 61 | 62 | @Concat 63 | @chr 64 | @lambda _: 37 65 | class PercentSign: 66 | pass 67 | 68 | 69 | @Concat 70 | @chr 71 | @lambda _: 38 72 | class Ampersand: 73 | pass 74 | 75 | 76 | @Concat 77 | @chr 78 | @lambda _: 39 79 | class Apostrophe: 80 | pass 81 | 82 | 83 | @Concat 84 | @chr 85 | @lambda _: 40 86 | class LeftParenthesis: 87 | pass 88 | 89 | 90 | @Concat 91 | @chr 92 | @lambda _: 41 93 | class RightParenthesis: 94 | pass 95 | 96 | 97 | @Concat 98 | @chr 99 | @lambda _: 42 100 | class Asterisk: 101 | pass 102 | 103 | 104 | @Concat 105 | @chr 106 | @lambda _: 43 107 | class Plus: 108 | pass 109 | 110 | 111 | @Concat 112 | @chr 113 | @lambda _: 44 114 | class Comma: 115 | pass 116 | 117 | 118 | @Concat 119 | @chr 120 | @lambda _: 45 121 | class Minus: 122 | pass 123 | 124 | 125 | @Concat 126 | @chr 127 | @lambda _: 46 128 | class Period: 129 | pass 130 | 131 | 132 | @Concat 133 | @chr 134 | @lambda _: 47 135 | class Slash: 136 | pass 137 | 138 | 139 | @Concat 140 | @chr 141 | @lambda _: 58 142 | class Colon: 143 | pass 144 | 145 | 146 | @Concat 147 | @chr 148 | @lambda _: 59 149 | class Semicolon: 150 | pass 151 | 152 | 153 | @Concat 154 | @chr 155 | @lambda _: 60 156 | class LessThan: 157 | pass 158 | 159 | 160 | @Concat 161 | @chr 162 | @lambda _: 61 163 | class Equal: 164 | pass 165 | 166 | 167 | @Concat 168 | @chr 169 | @lambda _: 62 170 | class GreaterThan: 171 | pass 172 | 173 | 174 | @Concat 175 | @chr 176 | @lambda _: 63 177 | class QuestionMark: 178 | pass 179 | 180 | 181 | @Concat 182 | @chr 183 | @lambda _: 64 184 | class AtSign: 185 | pass 186 | 187 | 188 | @Concat 189 | @chr 190 | @lambda _: 91 191 | class LeftBracket: 192 | pass 193 | 194 | 195 | @Concat 196 | @chr 197 | @lambda _: 92 198 | class Backslash: 199 | pass 200 | 201 | 202 | @Concat 203 | @chr 204 | @lambda _: 93 205 | class RightBracket: 206 | pass 207 | 208 | 209 | @Concat 210 | @chr 211 | @lambda _: 94 212 | class Caret: 213 | pass 214 | 215 | 216 | @Concat 217 | @chr 218 | @lambda _: 95 219 | class Underscore: 220 | pass 221 | 222 | 223 | @Concat 224 | @chr 225 | @lambda _: 96 226 | class Grave: 227 | pass 228 | 229 | 230 | @Concat 231 | @chr 232 | @lambda _: 123 233 | class LeftBrace: 234 | pass 235 | 236 | 237 | @Concat 238 | @chr 239 | @lambda _: 124 240 | class VerticalBar: 241 | pass 242 | 243 | 244 | @Concat 245 | @chr 246 | @lambda _: 125 247 | class RightBrace: 248 | pass 249 | 250 | 251 | @Concat 252 | @chr 253 | @lambda _: 126 254 | class Tilde: 255 | pass 256 | -------------------------------------------------------------------------------- /v2/src/quine.py: -------------------------------------------------------------------------------- 1 | @lambda _: 978607384200098875625253549315831058803968909030873526970432189614576132174030129840629697537582459734904727018463252167799895025277945917566882308347224515074630132151925013531544601766034594742740541234359290755929102453279962810355727048011220789362500957194735167817236002420126411243485021902161690654624818725264354973910165034748957101721984235535810930003685893798824707791172923439098369892544791449884442648076236653207924907715424529850166253191548672395027122817642103094508119644333229338416766813774986355939936040872489034336843282638627643428040197197365279761861755773855002722771728357031626001615655452242110626516434914020051998424152896202625671842918159182869153605602372180034780090701066048056939942605315551315501614359506183232677972286731439547092603042023900678753592310081821459122558526685535558060869194430068645876781012873823047347353068214781776000637704162706979186149187414892118170100968696069179969001643787699552509354478812197893205220837343276759712245553336988484507082794071500453203339216234926127945175790389385612884427189708743435012547425112688495140697113408397538595309685173831784160838471531684616951592331453570975433705491239568713611014865525287668788745175612278008004463596740685380402026304345921474159972506810553146071405124006245364722846413662097448549854761717233775364633002988275314761828496112756671899126038328250456428392022246031119015348582083203922801477887159946217670187131922751040535719313269183452649504708846853744100284739604018824149292783204016048831817388738667534692059117442649158119587420034712631358829565436492091649382825576991853528575904829435663134522795336774791047263043472457779344536763368165712756448337789584696343159311081103095654396330665276976492004625386208824215098721131272073363424159660219971767943984738948968962147163748668097455065931377001717190259325188701353696217983774715725139260303836859177635770439584534186442080035743721588608611104794484051485162846329784800337567281726024596453426650015460236007976649517600072266044377121273122474634461195240920564068772870516628281248372152661217438939605300651814113705420848181721540763181023990164572199592728855364876365437454756624884304570035909962861916827728667269318440650495606248646523268280433792969279469476816794068351490601313784220547016283500964507678106169917369635072214051058761907426106497582838525352676647333208567705382837102558556401332561780989056690890272340719695156100595797676972265260787536090439466484733526272106660024874453856758435254459410138404637725211645286176139642566575631644615271295488544962297960738800510820903552808605416839818095573820943893374484899924099869816152452527323399961299959147229330950310622328418146629032008833553463491301592698745958124690083469303403519745932102294956943265881689681203823216624163599092168080383845142995198138391172354214187941342873583558078588988981368256631174988986103323455974293214824122426548514843840023262745288540635645323169515441101245354825118776782316217453547109870526039644965749852613070411998581488820881821291746859703328139978602481609006149345792451122680279009133773864950229498811619493436472761961628868162684082669394285531827216466166659160802840762631453698292546847971344354083073419462114465657902050298905132049558897204031387515154271026279802808217348899086719669503679783762010241131968429323745336824984084853745048793461760281176030778654274838599544373049425447760147595996071102437054856940386780984269202792397059058995274432957714347445264893717813568726495756129231946162581470998940459052320524032346568843472113977143856327027683418835375408489529101619315561244513439603618294909423689179892857488162361371837310330797078765748326463660558515020486290196474499674721678662690686258690215734283590937774056452136785196992128841252842544395169632213516352890853303955800107666729954087340248006386719306584223444006527732732691626417570144413834508589109661101045051125335859372017950263710441165163473316984280144117350339268903295919577771107550691300613896597851329095386158396012577606630904336594129565197380494388616439023407338435168951482559108198947424575779060729158668877450204417841514262867255585003074708633828539811408170132978410723132040884748247801657991972988104280533488596839082574877945027741378250196400832940862467420258038296916392799770409691191616549004224527440556361424907582370319416310779717635907996844236223194503123331234376960981179509919204045946001385515525472500021243936088174632217956373268385935788574380661764215607237523224774621717967219462103511648229619782859233073701569437691012914078284327255247294443092282501361437945951846370097463575652093802864783980571760243631107840595007556796887605261815771468865124015426745909774347905563343662025898441742992764037413376135897138329367696745167786656788325615617138535540352976727511435842687430380754290967477246429757231546589005371373554571033308392618400823763773650026267627875043724570391837929803564551024440176200567897184234313426540692895076389184264685988799397888527453707967375171153295290565952692874877970760488023128752032078356566830371398405019201121106881573891686603907283637819611689740650597673292360208718713312843675644941844807364108568833834500419270987890154243687982516788236462460268907589834704041949002801599094557934282450720542526636243749216049415863128743663549333710602510289143631238462298556455213394652640411568780660513180948695328107432269474177294571370015814759569029885714474550910304068943283600212097564685611922710773880090489610 2 | class N: 3 | pass 4 | 5 | 6 | from operator import attrgetter 7 | from operator import itemgetter 8 | 9 | 10 | @itemgetter 11 | @slice 12 | @lambda _: 0 13 | class Clear: 14 | pass 15 | 16 | 17 | @itemgetter 18 | @slice 19 | @lambda _: 3 20 | class S3: 21 | pass 22 | 23 | 24 | @itemgetter 25 | @slice 26 | @lambda _: 4 27 | class S4: 28 | pass 29 | 30 | 31 | @itemgetter 32 | @slice 33 | @lambda _: 5 34 | class S5: 35 | pass 36 | 37 | 38 | @itemgetter 39 | @slice 40 | @lambda _: 6 41 | class S6: 42 | pass 43 | 44 | 45 | @itemgetter 46 | @slice 47 | @lambda _: 7 48 | class S7: 49 | pass 50 | 51 | 52 | @itemgetter 53 | @slice 54 | @lambda _: 8 55 | class S8: 56 | pass 57 | 58 | 59 | @itemgetter 60 | @slice 61 | @lambda _: 9 62 | class S9: 63 | pass 64 | 65 | 66 | @attrgetter 67 | @next 68 | @iter 69 | @dir 70 | @lambda _: str 71 | class Add: 72 | pass 73 | 74 | 75 | @itemgetter 76 | @lambda _: 56 77 | class Get: 78 | pass 79 | 80 | 81 | @attrgetter 82 | @Get 83 | @dir 84 | @lambda _: str 85 | class J: 86 | pass 87 | 88 | 89 | @J 90 | @Clear 91 | @str 92 | class Join: 93 | pass 94 | 95 | 96 | @Add 97 | @chr 98 | @lambda _: 32 99 | class Space: 100 | pass 101 | 102 | 103 | @Add 104 | @chr 105 | @lambda _: 34 106 | class Quote: 107 | pass 108 | 109 | 110 | @Add 111 | @chr 112 | @lambda _: 40 113 | class LeftP: 114 | pass 115 | 116 | 117 | @Add 118 | @chr 119 | @lambda _: 41 120 | class RightP: 121 | pass 122 | 123 | 124 | @Add 125 | @chr 126 | @lambda _: 44 127 | class Comma: 128 | pass 129 | 130 | 131 | @Add 132 | @chr 133 | @lambda _: 46 134 | class Dot: 135 | pass 136 | 137 | 138 | @Add 139 | @chr 140 | @lambda _: 50 141 | class Two: 142 | pass 143 | 144 | 145 | @Add 146 | @chr 147 | @lambda _: 58 148 | class Colon: 149 | pass 150 | 151 | 152 | @Add 153 | @chr 154 | @lambda _: 59 155 | class End: 156 | pass 157 | 158 | 159 | @Add 160 | @chr 161 | @lambda _: 61 162 | class Equal: 163 | pass 164 | 165 | 166 | @Add 167 | @chr 168 | @lambda _: 91 169 | class LeftB: 170 | pass 171 | 172 | 173 | @Add 174 | @chr 175 | @lambda _: 93 176 | class RightB: 177 | pass 178 | 179 | 180 | @Add 181 | @chr 182 | @lambda _: 95 183 | class Low: 184 | pass 185 | 186 | 187 | @Add 188 | @Join 189 | @S6 190 | @list 191 | @reversed 192 | @S8 193 | @list 194 | @reversed 195 | @str 196 | class _lambda: 197 | pass 198 | 199 | 200 | @Add 201 | @Join 202 | @S3 203 | @list 204 | @reversed 205 | @S5 206 | @list 207 | @reversed 208 | @str 209 | class _end: 210 | pass 211 | 212 | 213 | @Add 214 | @Join 215 | @S5 216 | @list 217 | @reversed 218 | @S7 219 | @list 220 | @reversed 221 | @str 222 | class _print: 223 | pass 224 | 225 | 226 | @Add 227 | @Join 228 | @S3 229 | @list 230 | @reversed 231 | @S5 232 | @list 233 | @reversed 234 | @str 235 | class _str: 236 | pass 237 | 238 | 239 | @Add 240 | @Join 241 | @S5 242 | @list 243 | @reversed 244 | @S7 245 | @list 246 | @reversed 247 | @str 248 | class _bytes: 249 | pass 250 | 251 | 252 | @Add 253 | @Join 254 | @S7 255 | @list 256 | @reversed 257 | @S9 258 | @list 259 | @reversed 260 | @str 261 | class _fromhex: 262 | pass 263 | 264 | 265 | @Add 266 | @Join 267 | @S3 268 | @list 269 | @reversed 270 | @S5 271 | @list 272 | @reversed 273 | @str 274 | class _hex: 275 | pass 276 | 277 | 278 | @Add 279 | @Join 280 | @S3 281 | @list 282 | @reversed 283 | @S5 284 | @list 285 | @reversed 286 | @str 287 | class _utf: 288 | pass 289 | 290 | 291 | @Add 292 | @chr 293 | @lambda _: 78 294 | class _N: 295 | pass 296 | 297 | 298 | @exec 299 | @_print 300 | @LeftP 301 | @Quote 302 | @_lambda 303 | @Space 304 | @Low 305 | @Colon 306 | @Quote 307 | @Comma 308 | @_N 309 | @RightP 310 | @End 311 | @_print 312 | @LeftP 313 | @_str 314 | @LeftP 315 | @_bytes 316 | @Dot 317 | @_fromhex 318 | @LeftP 319 | @_hex 320 | @LeftP 321 | @_N 322 | @RightP 323 | @LeftB 324 | @Two 325 | @Colon 326 | @RightB 327 | @RightP 328 | @Comma 329 | @Quote 330 | @_utf 331 | @Quote 332 | @RightP 333 | @RightP 334 | @Clear 335 | @str 336 | class M: 337 | pass 338 | -------------------------------------------------------------------------------- /v2/src/quine_golfed.py: -------------------------------------------------------------------------------- 1 | @lambda _: 0x636c617373204e3a300a66726f6d206f70657261746f7220696d706f7274206174747267657474657220617320480a66726f6d206f70657261746f7220696d706f7274206974656d67657474657220617320490a406c616d626461205f3a736c6963650a636c61737320533a300a406c616d626461205f3a72657665727365640a636c61737320563a300a406c616d626461205f3a6c6973740a636c617373204d3a300a406c616d626461205f3a6368720a636c61737320433a300a40490a40530a406c616d626461205f3a300a636c617373207a3a300a40490a40530a406c616d626461205f3a330a636c61737320743a330a40490a40530a406c616d626461205f3a340a636c61737320663a340a40490a40530a406c616d626461205f3a350a636c61737320673a350a40490a40530a406c616d626461205f3a360a636c61737320733a360a40490a40530a406c616d626461205f3a370a636c61737320753a370a40490a40530a406c616d626461205f3a380a636c61737320653a380a40490a40530a406c616d626461205f3a390a636c617373206e3a390a40480a406e6578740a40697465720a406469720a406c616d626461205f3a7374720a636c61737320413a300a40490a406c616d626461205f3a35370a636c61737320473a300a40480a40470a406469720a406c616d626461205f3a7374720a636c617373204b3a300a404b0a407a0a407374720a636c617373204a3a300a40410a404a0a40730a404d0a40560a40650a404d0a40560a407374720a636c617373205f6c616d6264613a300a40410a404a0a40670a404d0a40560a40750a404d0a40560a407374720a636c617373205f7072696e743a300a40410a404a0a40740a404d0a40560a40670a404d0a40560a407374720a636c617373205f7374723a300a40410a404a0a40670a404d0a40560a40750a404d0a40560a407374720a636c617373205f62797465733a300a40410a404a0a40750a404d0a40560a406e0a404d0a40560a407374720a636c6173732066726f6d6865783a300a40410a404a0a40740a404d0a40560a40670a404d0a40560a407374720a636c617373205f6865783a300a40410a404a0a40740a404d0a40560a40670a404d0a40560a407374720a636c617373205f7574663a300a40410a40430a406c616d626461205f3a37380a636c617373205f4e3a300a40410a40430a406c616d626461205f3a33320a636c61737320703a300a40410a40430a406c616d626461205f3a33340a636c61737320713a300a40410a40430a406c616d626461205f3a34300a636c617373206a3a300a40410a40430a406c616d626461205f3a34310a636c617373206b3a300a40410a40430a406c616d626461205f3a34340a636c61737320633a300a40410a40430a406c616d626461205f3a34360a636c617373206f3a300a40410a40430a406c616d626461205f3a35300a636c61737320773a300a40410a40430a406c616d626461205f3a35380a636c617373206c3a300a40410a40430a406c616d626461205f3a35390a636c61737320643a300a40410a40430a406c616d626461205f3a36310a636c61737320613a300a40410a40430a406c616d626461205f3a36340a636c61737320623a300a40410a40430a406c616d626461205f3a39310a636c61737320783a300a40410a40430a406c616d626461205f3a39330a636c61737320793a300a40410a40430a406c616d626461205f3a39350a636c61737320723a300a40657865630a405f7072696e740a406a0a40710a40620a405f6c616d6264610a40700a40720a406c0a40710a40630a405f6865780a406a0a405f4e0a406b0a406b0a40640a405f7072696e740a406a0a405f7374720a406a0a405f62797465730a406f0a4066726f6d6865780a406a0a405f6865780a406a0a405f4e0a406b0a40780a40770a406c0a40790a406b0a40630a40710a405f7574660a40710a406b0a406b0a407a0a407374720a636c617373205f3a30 2 | class N:0 3 | from operator import attrgetter as H 4 | from operator import itemgetter as I 5 | @lambda _:slice 6 | class S:0 7 | @lambda _:reversed 8 | class V:0 9 | @lambda _:list 10 | class M:0 11 | @lambda _:chr 12 | class C:0 13 | @I 14 | @S 15 | @lambda _:0 16 | class z:0 17 | @I 18 | @S 19 | @lambda _:3 20 | class t:3 21 | @I 22 | @S 23 | @lambda _:4 24 | class f:4 25 | @I 26 | @S 27 | @lambda _:5 28 | class g:5 29 | @I 30 | @S 31 | @lambda _:6 32 | class s:6 33 | @I 34 | @S 35 | @lambda _:7 36 | class u:7 37 | @I 38 | @S 39 | @lambda _:8 40 | class e:8 41 | @I 42 | @S 43 | @lambda _:9 44 | class n:9 45 | @H 46 | @next 47 | @iter 48 | @dir 49 | @lambda _:str 50 | class A:0 51 | @I 52 | @lambda _:57 53 | class G:0 54 | @H 55 | @G 56 | @dir 57 | @lambda _:str 58 | class K:0 59 | @K 60 | @z 61 | @str 62 | class J:0 63 | @A 64 | @J 65 | @s 66 | @M 67 | @V 68 | @e 69 | @M 70 | @V 71 | @str 72 | class _lambda:0 73 | @A 74 | @J 75 | @g 76 | @M 77 | @V 78 | @u 79 | @M 80 | @V 81 | @str 82 | class _print:0 83 | @A 84 | @J 85 | @t 86 | @M 87 | @V 88 | @g 89 | @M 90 | @V 91 | @str 92 | class _str:0 93 | @A 94 | @J 95 | @g 96 | @M 97 | @V 98 | @u 99 | @M 100 | @V 101 | @str 102 | class _bytes:0 103 | @A 104 | @J 105 | @u 106 | @M 107 | @V 108 | @n 109 | @M 110 | @V 111 | @str 112 | class fromhex:0 113 | @A 114 | @J 115 | @t 116 | @M 117 | @V 118 | @g 119 | @M 120 | @V 121 | @str 122 | class _hex:0 123 | @A 124 | @J 125 | @t 126 | @M 127 | @V 128 | @g 129 | @M 130 | @V 131 | @str 132 | class _utf:0 133 | @A 134 | @C 135 | @lambda _:78 136 | class _N:0 137 | @A 138 | @C 139 | @lambda _:32 140 | class p:0 141 | @A 142 | @C 143 | @lambda _:34 144 | class q:0 145 | @A 146 | @C 147 | @lambda _:40 148 | class j:0 149 | @A 150 | @C 151 | @lambda _:41 152 | class k:0 153 | @A 154 | @C 155 | @lambda _:44 156 | class c:0 157 | @A 158 | @C 159 | @lambda _:46 160 | class o:0 161 | @A 162 | @C 163 | @lambda _:50 164 | class w:0 165 | @A 166 | @C 167 | @lambda _:58 168 | class l:0 169 | @A 170 | @C 171 | @lambda _:59 172 | class d:0 173 | @A 174 | @C 175 | @lambda _:61 176 | class a:0 177 | @A 178 | @C 179 | @lambda _:64 180 | class b:0 181 | @A 182 | @C 183 | @lambda _:91 184 | class x:0 185 | @A 186 | @C 187 | @lambda _:93 188 | class y:0 189 | @A 190 | @C 191 | @lambda _:95 192 | class r:0 193 | @exec 194 | @_print 195 | @j 196 | @q 197 | @b 198 | @_lambda 199 | @p 200 | @r 201 | @l 202 | @q 203 | @c 204 | @_hex 205 | @j 206 | @_N 207 | @k 208 | @k 209 | @d 210 | @_print 211 | @j 212 | @_str 213 | @j 214 | @_bytes 215 | @o 216 | @fromhex 217 | @j 218 | @_hex 219 | @j 220 | @_N 221 | @k 222 | @x 223 | @w 224 | @l 225 | @y 226 | @k 227 | @c 228 | @q 229 | @_utf 230 | @q 231 | @k 232 | @k 233 | @z 234 | @str 235 | class _:0 236 | -------------------------------------------------------------------------------- /v2/src/util/__init__.py: -------------------------------------------------------------------------------- 1 | from operator import attrgetter 2 | from operator import itemgetter 3 | 4 | 5 | @itemgetter 6 | @lambda _: 35 7 | class _35: 8 | pass 9 | 10 | 11 | @attrgetter 12 | @_35 13 | @dir 14 | @lambda _: str 15 | class _Casefold: 16 | pass 17 | 18 | 19 | @_Casefold 20 | @lambda _: str 21 | class Casefold: 22 | pass 23 | 24 | 25 | @attrgetter 26 | @next 27 | @iter 28 | @dir 29 | @lambda _: str 30 | class Concat: 31 | pass 32 | 33 | 34 | @itemgetter 35 | @slice 36 | @lambda _: 0 37 | class Clear: 38 | pass 39 | 40 | 41 | @Clear 42 | @str 43 | class EmptyString: 44 | pass 45 | 46 | 47 | @itemgetter 48 | @lambda _: 57 49 | class _57: 50 | pass 51 | 52 | 53 | @attrgetter 54 | @_57 55 | @dir 56 | @lambda _: str 57 | class _Join: 58 | pass 59 | 60 | 61 | @_Join 62 | @lambda _: EmptyString 63 | class Join: 64 | pass 65 | 66 | 67 | @itemgetter 68 | @lambda _: 43 69 | class _43: 70 | pass 71 | 72 | 73 | @attrgetter 74 | @_43 75 | @dir 76 | @lambda _: bytes 77 | class _FromHex: 78 | pass 79 | 80 | 81 | @_FromHex 82 | @lambda _: bytes 83 | class FromHex: 84 | pass 85 | 86 | 87 | @itemgetter 88 | @lambda _: 39 89 | class _39: 90 | pass 91 | 92 | 93 | @attrgetter 94 | @_39 95 | @dir 96 | @lambda _: bytes 97 | class _Decode: 98 | pass 99 | 100 | 101 | @_Decode 102 | @lambda _: bytes 103 | class Decode: 104 | pass 105 | 106 | 107 | @iter 108 | @hex 109 | @lambda _: 0x6c616d626461205f3a6576616c2862797465732e66726f6d6865782866227b5f3a787d22292e6465636f6465282929 110 | class _EvalHex: 111 | pass 112 | 113 | 114 | @eval 115 | @Decode 116 | @FromHex 117 | @Join 118 | @list 119 | @lambda _: _EvalHex 120 | @next 121 | @lambda _: _EvalHex 122 | @next 123 | @lambda _: _EvalHex 124 | class EvalHex: 125 | pass 126 | 127 | 128 | @EvalHex 129 | @lambda _: 0x747970652822222c28292c7b225f5f696e69745f5f223a6c616d62646120732c5f3a7365746174747228732c225f222c5f292c225f5f6d61746d756c5f5f223a6c616d62646120732c5f3a732e5f285f292c225f5f726d61746d756c5f5f223a6c616d62646120732c5f3a74797065287329285f297d29 130 | class _Call: 131 | pass 132 | 133 | 134 | @_Call 135 | class Call: 136 | pass 137 | 138 | 139 | @EvalHex 140 | @lambda _: 0x747970652822222c28292c7b225f5f696e69745f5f223a6c616d62646120732c5f3a7365746174747228732c225f222c5f292c225f5f6d61746d756c5f5f223a6c616d62646120732c5f3a4e6f74496d706c656d656e746564206966206973696e7374616e6365285f2c5f43616c6c29656c7365207479706528732928732e5f2b5b5f5d297d29 141 | class _Builder: 142 | pass 143 | 144 | 145 | @EvalHex 146 | @lambda _: 0x747970652822222c285f4275696c6465722c292c7b225f5f63616c6c5f5f223a6c616d62646120732c5f3a5b5f3a3d66285f29666f72206620696e20732e5f5d5b2d315d7d29 147 | class _Compose(_Builder): 148 | pass 149 | 150 | 151 | @_Compose 152 | @Clear 153 | @list 154 | @str 155 | class Compose: 156 | pass 157 | 158 | 159 | @EvalHex 160 | @lambda _: 0x747970652822222c285f4275696c6465722c292c7b225f5f63616c6c5f5f223a6c616d62646120732c5f3a5f282a732e5f297d29 161 | class _Args(_Builder): 162 | pass 163 | 164 | 165 | @_Args 166 | @Clear 167 | @list 168 | @str 169 | class Args: 170 | pass 171 | 172 | 173 | @itemgetter 174 | @lambda _: 28 175 | class _28: 176 | pass 177 | 178 | 179 | @attrgetter 180 | @_28 181 | @dir 182 | @lambda _: type 183 | class GetName: 184 | pass 185 | 186 | 187 | @EvalHex 188 | @lambda _: 0x696e742e5f5f6e65675f5f 189 | class Negate: 190 | pass 191 | 192 | 193 | @Negate 194 | @lambda _: 1 195 | class _1: 196 | pass 197 | 198 | 199 | @itemgetter 200 | @lambda _: Args @ None @ None @ _1 @ Call @ slice 201 | class Reverse: 202 | pass 203 | 204 | 205 | @EvalHex 206 | @lambda _: 0x6c616d6264612a5f3a6c697374285f29 207 | class ListOf: 208 | pass 209 | -------------------------------------------------------------------------------- /v4/README.md: -------------------------------------------------------------------------------- 1 | # Run-on Python v4 2 | 3 | ## The Main Tricks 4 | We'll first establish the most important tricks in removing punctuation from your code. These will actually do most of the heavy lifting for us when it comes to removing marks, particularly any and all operators. 5 | 6 | ### Everything is Magic 7 | Every operator in Python is converted into a certain *magic method call* when interpreted. 8 | * `x % y` becomes `x.__mod__(y)` 9 | * `x += y` becomes `x.__iadd__(y)` 10 | 11 | and so on[^1]. You can find a list of all of them and their correspondences [here](https://docs.python.org/3/reference/datamodel.html). 12 | 13 | [^1]: Sometimes these default to the *right* operand, becoming `y.__rmod__(x)`, but this is hardly relevant to our purposes. 14 | 15 | But it should be evident that this fact lets us ditch every operator right from the get-go, since they get effectively removed anyway. 16 | 17 | ### Everything is `getattr` 18 | In removing every operator, we've made it almost abundantly necessary to have a `.` at our disposal, as this is how we interface with the magic methods (and indeed, any class methods at all). However, Guido must've had our particular endeavor in mind all those years ago, as we are graced with the `getattr` and `setattr` methods. These methods are the true backbones of Python, as we have the following equivalences: 19 | * `x.y` becomes `getattr(x, "y")` 20 | * `x.y = z` becomes `setattr(x, "y", z)` 21 | 22 | So not only is `.` out of the picture, `=` appears to be as well; we just need a way to set a simple local variable, one that isn't an attribute. Luckily, local variables are actually stored in a giant dictionary, `locals()`[^2][^3], that we can modify using `__setitem__`. That is, we have 23 | * `x = y` becomes `locals().__setitem__("x", y)` becomes `getattr(locals(), "__setitem__")("x", y)` 24 | 25 | [^2]: Or sometimes `globals()`, which will be important later. 26 | [^3]: Or sometimes [not at all](https://docs.python.org/3/library/functions.html#locals). 27 | 28 | What's most interesting about the above is that there is a sense in which `x = y` *literally means* the expanded form on the right. Overiding `__setitem__` for the built-in `dict` class would apply, as would messing about with `getattr` (both of which are highly frowned upon outside shenanigans like this). 29 | 30 | ### The Pythonic Comma 31 | Now surely, we must be insane to think we can be rid of the `,`. Every call to `getattr` requires one, and `setattr` needs two! But fear not, for there is one more trick up our sleeve: the ternary operator. 32 | 33 | In C and its many children, the ternary operator looks like `a ? b : c`, which means "evaluate `a`, and return `b` if it is True and `c` otherwise". 34 | 35 | Python being the little rascal that it is managed to forego punctuation altogether for its version: `b if a else c`. 36 | 37 | We can now employ the ternary operator to replace the comma in the following way. First, we use it to construct two-element tuples (or lists, whichever you fancy): 38 | ```python 39 | (x, y) == tuple(y if i else x for i in range(2)) 40 | ``` 41 | 42 | Note the subtlety of this trick: we have to use the truthiness of `i = 1` to distinguish `x` and `y` in the tuple construction *without relying on any other operators*, as otherwise we'd be right back to needing a comma in the function call! 43 | 44 | Next, we construct longer tuples. We'll simply use the `__add__` method for tuples along with the tricks we already know[^4]: 45 | ```python 46 | (x, y, z) == getattr(*(y if i else x for i in range(2)), "__add__")(tuple(z for _ in range(1))) 47 | ``` 48 | We can extend this indefinitely, and also make it a bit less messy by using local variables (i.e. `locals()` members) along the way. 49 | 50 | [^4]: We can drop `tuple` from unpacking statements, since generators can be unpacked as well, saving us a few letters. 51 | 52 | The coup-de-grace is then Python's wonderful fellow `*`, the unpacking operator. Luckily, almost every operator is binary, so we don't need to do many more shenanigans to get all the boilerplate arguments we need into a tuple. 53 | ```python 54 | getattr(x, "y") == getattr(*("y" if i else x for i in range(2))) 55 | ``` 56 | and thus 57 | ```python 58 | (x, y, z) == getattr(*("__add__" if i else tuple(y if i else x for i in range(2)) for i in range(2)))(tuple(z for _ in range(1))) 59 | ``` 60 | 61 | In the case of `getattr`, arguably the most common call of all, the built-in `operator` module can also save the day via `attrgetter`: 62 | ```python 63 | getattr(x, "y") == operator.attrgetter("y")(x) 64 | ``` 65 | This particular convenience makes use of inner functions, which will prove to be our best friends further down the road. 66 | 67 | ### You Knew `chr` Was Coming 68 | Alright, we've (almost completely) taken care of every mark on our list except `"`. Luckily, we can turn to the `chr` function to save the day: this little bugger takes in an ASCII code as an integer and returns the corresponding character. Thus, we can build up any string we like using only `()`, as `chr` is a single-argument function. 69 | 70 | ```python 71 | getattr(x, "y") == getattr(*(chr(121) if i else x for i in range(2))) 72 | ``` 73 | 74 | As with tuples, any length of string is possible in principle, but we need to be crafty: recall that in order to build longer tuples, we needed the `__add__` method. But in order to access it, we used `getattr(tuple, "__add__")`, which already has a string in it! 75 | 76 | The key is the wonderful `dir` function, which returns a list of the *names* every attribute of an object[^5]. By turning this list into an iterator, we can `next` our way to *any* element, and then use that name as the argument to `getattr`! We might need *many* `next` calls, but they are all well within the rules. To keep from relying on the method order of the built-ins[^6], we can define a custom class: 77 | 78 | ```python 79 | class F: 80 | def __add__(self): 81 | pass 82 | 83 | def __setitem__(self): 84 | pass 85 | ``` 86 | 87 | [^5]: Specifically, `dir(foo)` is equivalent to `foo.__dir__`, which *very conveniently* dodges that `.`. 88 | [^6]: This order is alphabetical, but could be adjusted slightly as new methods get added with each release. 89 | 90 | Such a class has the added benefit of containing relatively few default methods that get in the way, which you can see by running `dir` on an empty class: 91 | 92 | ```python 93 | class X: 94 | pass 95 | 96 | dir(X) == ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__'] 97 | ``` 98 | 99 | So, once we can nab `__setitem__` and its friends all at once, we have it at our disposal to build arbitrary strings. 100 | 101 | ### None of This Actually Works 102 | 103 | Now, I must be honest with you: the tricks that we've seen so far are great, but we're still quite far from getting something functional. Trying to employ most of them directly fails for two main reasons: nested unpacking and the true meaning of `locals()`. 104 | 105 | For reasons that are [purely aesthetic](https://peps.python.org/pep-0448/#variations), Python won't let you unpack within a comprehension. Thus, we can only use our tuple trick once or twice before needing to store the value away; in particular, we can never call a multi-argument function within another multi-argument function call, which really wrecks our ability to use `getattr` well. 106 | 107 | Our problems are furthermore compounded by the fact that `locals()` is actually unusable with our new tricks. Consider the following, seemingly identical snippets: 108 | 109 | ```python 110 | locals().__setitem__("S", "__setitem__") 111 | getattr(*("__setitem__" if i else locals() for i in range(2)))("S", "__setitem__") 112 | ``` 113 | 114 | The first of those lines does exactly what you'd expect: saves `'__setitem__'` to `S` so we can use it later. The second, however, doesn't, because of the following devilish detail: comprehensions define new variable scopes. Once inside a comprehension, `locals()` refers not to the enclosing scope you came from, but the scope of the comprehension itself! Thus, setting the "local" value of `S` is useless, since it does not live outside the comprehension. 115 | 116 | Luckily, there's an easy but inelegant fix: use `globals()` instead. Namespace purists may rage, but it's the only option we've got. And now that we've got `globals()`, we can store away values in-between unpackings to keep Guido happy. 117 | 118 | ### Putting It All Together 119 | 120 | So, let's build our way to a working Run-on Python implementation. We've got no classes, long strings, or anything else at our disposal, and we'll also refrain from importing anything at the moment. 121 | 122 | We're first gonna want some helper functions. The most crucial is the "twopler", a function with can curry two inputs to build a tuple using the power of inner functions: 123 | 124 | ```python 125 | def tup(first): 126 | def inner(second): 127 | return tuple(second if i else first for i in range(2)) 128 | return inner 129 | ``` 130 | 131 | Thus, we can avoid unpacking within a comprehension, since we won't even be using a raw comprehension to build our tuples! 132 | 133 | Next, we'll define a function that can call `next` an arbitrary number of times. Note that we need to do this *without* being able to store the iterator somewhere directly (since we don't have `__setitem__` yet), but luckily we can leverage the fact that functions bind their arguments to their parameter names: 134 | 135 | ```python 136 | def advance(num): 137 | def inner(iterator): 138 | for _ in range(num): 139 | next(iterator) 140 | return iterator 141 | return inner 142 | ``` 143 | 144 | With it, we can nab our method names from the class `F` defined earlier: 145 | 146 | ```python 147 | '__add__' == next(iter(dir(F))) 148 | '__setitem__' == next(advance(23)(iter(dir(F)))) 149 | ``` 150 | 151 | A function that simplifies `__setitem__` calls would also be nice (recall that we have to always use `globals()` for variables): 152 | 153 | ```python 154 | def set_value(name): 155 | def inner(value): 156 | return getattr(*tup(globals())(next(advance(23)(iter(dir(F))))))(*tup(name)(value)) 157 | return inner 158 | ``` 159 | 160 | We can use this helper to put our method names into convenient, single-letter variables, along with `range(1)` and `range(2)` for convenience: 161 | 162 | ```python 163 | set_value(chr(65))(next(iter(dir(F)))) 164 | set_value(chr(83))(next(advance(23)(iter(dir(F))))) 165 | 166 | set_value(chr(79))(range(1)) 167 | set_value(chr(84))(range(2)) 168 | ``` 169 | 170 | Finally, we'll give ourselves the ultimate builder function. This wonderful fellow can assemble *any* iterable that implements `__add__`, most importantly strings; we can use it by first calling with a type and initial value, then each element in order, and then an empty call: 171 | ```python 172 | def build(t): 173 | def init(initial): 174 | def inner(*element): 175 | if not element: 176 | return initial 177 | return init(getattr(*tup(t)(A))(*tup(initial)(next(iter(element))))) 178 | return inner 179 | return init 180 | ``` 181 | 182 | Presto! We've got ourselves an implementation of Run-on Python that isn't completely horrendous to use. You can find all of this code plus some additional helper objects in `src/util/__init__py` (and thus you can import `util` to start your next project!). 183 | 184 | Only one last step to round off our endeavor. 185 | 186 | ## How To Make Like a Poet and Rid Yourself of Punctuation 187 | We will now systematically go through each of our 24 excess marks to reason why they are not necessary. Buckle up, cause this could take a while. 188 | 189 | ### `!` 190 | * `!=` is equivalent to `__neq__` or `not in` with a singleton tuple. 191 | 192 | ### `"` 193 | * **String literals** can be built using `chr`. 194 | * **Docstrings** are never necessary to run code, and are entirely ignored by the interpreter. 195 | * **f-strings** can always be implemented directly; `str.format` can also take a bit of the load. 196 | 197 | ### `#` 198 | * **Comments** are never necessary to run code, and are entirely ignored by the interpreter. 199 | 200 | ### `%` 201 | * `%` is equivalent to `__mod__`. 202 | * `%=` is equivalent to `__imod__`. 203 | * **Format strings** that use `%` can be identically achieved using f-strings. 204 | 205 | ### `&` 206 | * `&` is equivalent to `__and__`. 207 | * `&=` is equivalent to `__rand__`. 208 | 209 | ### `'` 210 | * `'` is equivalent to `"`, which we know is moot. 211 | 212 | ### `+` 213 | * `+` is equivalent to `__add__` as a binary operator and `__pos__` as a unary operator. 214 | * `+=` is equivalent to `__iadd__`. 215 | 216 | ### `,` 217 | * Multiple **function arguments** can be replaced by a single, iterable argument, or by currying with inner functions. 218 | * Raw **iterables** can be constructed using the ternary tuple trick. 219 | * Multiple **globals** or **nonlocals** on a single line can simply be split into multiple lines. 220 | * Multiple **imports** on a single line can simply be split into multiple lines. [PEP-8](https://peps.python.org/pep-0008/) even says you *should*. 221 | * **Unpacking** can always be done across multiple lines or statements. 222 | * **Multidimensional slices** are passed as tuples of `slice` objects into `__getitem__`, which can be resolved using known tricks. 223 | * `with` statements with **multiple targets** are [semantically equivalent](https://docs.python.org/3/reference/compound_stmts.html#the-with-statement) to nestings of singular targets. 224 | 225 | ### `-` 226 | * `-` is equivalent to `__sub__` as a binary operator and `__neg__` as a unary operator. 227 | * `-=` is equivalent to `__isub__`. 228 | * **Return type hints** are never necessary for function definitions. 229 | 230 | ### `.` 231 | * **Attributes** can be accessed via `getattr` and `setattr`. 232 | * **Decimals** can be written by `__div__`-ing integers by powers of 10, which will have the same viable precision as standard float literals. 233 | * **Relative imports** can be translated into calls to `__import__`. 234 | * **Ellipses** are equivalent to the identifier `Ellipsis`. 235 | 236 | ### `/` 237 | * `/` is equivalent to `__div__` (or `__truediv__` if you're [messing about in 2.7](https://docs.python.org/2.7/reference/datamodel.html#object.__truediv__)). 238 | * `/=` is equivalent to `__idiv__` (or `__itruediv__`). 239 | * `//` is equivalent to `__floordiv__`. 240 | * `//=` is equivalent to `__ifloordiv__`. 241 | * **Positional-only parameters** are never necessary for function definitions. 242 | 243 | ### `;` 244 | * **Newlines** are always sufficient to end a line, rendering `;` entirely unnecessary and really just a holdover for the C plebs. 245 | 246 | ### `<` 247 | * `<` is equivalent to `__lt__`. 248 | * `<=` is equivalent to `__le__`. 249 | * `<<` is equivalent to `__lshift__`. 250 | * `<<=` is equivalent to `__ilshift__`. 251 | 252 | ### `=` 253 | * `==` is equivalent to `__eq__` or `in` with a singleton tuple. 254 | * **Assignments** can be replaced by direct calls to `__setitem__` in `locals()` or `globals()`. 255 | * **Class definitions** are equivalent to `type`. 256 | * **Default function arguments** can be replaced with explicit passes of `None` followed by replacements in the function body. 257 | * **Keyword arguments** can be passed using dictionaries. 258 | 259 | ### `>` 260 | * `>` is equivalent to `__gt__`. 261 | * `>=` is equivalent to `__ge__`. 262 | * `>>` is equivalent to `__rshift__`. 263 | * `>>=` is equivalent to `__irshift__`. 264 | 265 | ### `@` 266 | * `@` is equivalent to `__matmul__`. 267 | * **Decorators** are equivalent to calls to explicit outer functions. 268 | 269 | ### `[]` 270 | * **List literals** can be built from tuples or generators. 271 | * **Subscripting** and **slicing** are equivalent to `__getitem__` and `__setitem__` via `slice`. 272 | * **Type hints** are never necessary for function definitions or assignment statements. 273 | 274 | ### `\ ` 275 | * **Escape sequences** can be replaced by explicit `chr` calls. 276 | 277 | ### `^` 278 | * `^` is equivalent to `__xor__`. 279 | * `^=` is equivalent to `__ixor__`. 280 | 281 | ### `{}` 282 | * **Dictionary literals** can be built from tuples or generators of tuples. 283 | * **Set literals** can be built from tuples or generators. 284 | 285 | ### `|` 286 | * `|` is equivalent to `__or__`. 287 | * `|=` is equivalent to `__ior__`. 288 | 289 | ### `~` 290 | * `~` is equivalent to `__inv__`. 291 | 292 | ## A Few Objections 293 | 294 | * "If everything gets stored in `globals()`, how can you do recursion?" 295 | * The answer is a call stack or something like it. Surely you've implemented one yourself before, right? 296 | * "How do you dictionaries? Like, actually." 297 | * Here's a snippet to build the dictionary `d = {0: 1, 2: 3}`: 298 | ```python 299 | set_value(chr(100))(dict(tup(tup(0)(1))(tup(2)(3)))) 300 | ``` 301 | * "What if you mess with the built-ins?" 302 | * @iPhoenix devised such examples, but such a point is rather moot, as messing with the built-ins enough breaks Python normally! 303 | * Thus, this project assumes you're not doing anything heinous *before* getting rid of your punctuation marks. 304 | * "Aren't classes kinda impossible since `self` is always the first argument of a method?" 305 | * Fear not, for `*args` can save us! The object will get collected just like any other argument, and from there you just need to parse. 306 | * Here's how to initialize an object that has a member `x` passed as the "first" argument: 307 | ```python 308 | class C: 309 | def __init__(*args): 310 | setattr(*build(tuple)(tup(next(iter(args)))(chr(120)))(tuple(next(advance(1)(iter(args))) for _ in range(1)))()) 311 | 312 | set_value(chr(99))(C(5)) 313 | ``` 314 | * "I can't access values by their names, what gives?" 315 | * Oh, yeah... see `set_value` modifies the `globals()` *for `util/__init__.py`*, meaning the interpreter won't be able to find any values set from outside the import by name. 316 | * Instead, you can use the included `get_value` helper, which accesses the same `globals()` scope and thus can find all your variables at no additional charge. 317 | 318 | ## Can We Do Any Better? 319 | With only four punctuation marks remaining, it seems unlikely we can get rid of any more. Indeed, I posit that this is true, *if* `exec` is barred from play: 320 | * `()` are the main way to do function calls, and are also the only grouping symbol left. Seems like a no-brainer that those have to stay. 321 | * As pointed out by @commandblockguy, decorators can also call things, as can the `del` operator, but then we run into the problem of defining those things in the first place. 322 | * It is also possible to dodge parentheses entirely [in some situations](https://polygl0ts.ch/writeups/2021/b01lers/pyjail_noparens/README.html), again using decorators, but we need to take on even more symbols to make it happen. 323 | * `:` is required for just about every control flow construct. Also pretty necessary. 324 | * *If* you're okay with using a Turing-complete subset of Python, then I think you can dodge `:` by living entirely inside tuple comprehensions. See the mission statement section for why we're not doing this. 325 | * `*` is very powerful, enabling us to drop `,` among other things. Unpacking is just too good to pass up. 326 | * Of course, keeping `,` over `*` is *much* cleaner, but dodging `,` is just too much fun. 327 | * Lambda calculus can also be implemented using only `()` and `:`, but again, that's not Python, just a whole lot of currying. 328 | 329 | [^7]: A separate gist of this file (which might later become its permanent home) can be found [here](https://gist.github.com/kg583/74dcf08574bb37f13be6fd978279bd6e). 330 | 331 | ## How Can I Write Run-on Python? 332 | I'm glad you asked. At the moment there are plans for a transpiler which can walk a given Python AST and burn away those blasphemous punctuation marks, but no implementation is yet available. Some parts are pretty easy, such as recognizing instances where we can use one of our primary tricks. Other snippets are much trickier if the user isn't kind, requiring more complex restructuring of the AST before compiling back down to valid code. 333 | 334 | The transpiler is still almost certainly doable in its entirety though, and if enough people pester me about it it'll get written. But for now, you can use this spec to write such code yourself, and make the world a slightly more confusing place. 335 | -------------------------------------------------------------------------------- /v4/src/classes.py: -------------------------------------------------------------------------------- 1 | from util import * 2 | 3 | 4 | class C: 5 | def __init__(*args): 6 | setattr(*build_tuple(f(args))(chr(120))(s(args))()) 7 | 8 | def __mul__(*args): 9 | return getattr(*tup(int)(A))(*tup(getattr(*tup(f(args))(chr(120))))(getattr(*tup(s(args))(chr(120))))) 10 | 11 | def this_is_a_property(self): 12 | return getattr(*tup(int)(A))(*tup(getattr(*tup(self)(chr(120))))(27)) 13 | 14 | 15 | setattr(*build_tuple(C)(f(reversed(dir(C))))(property(getattr(*tup(C)(f(reversed(dir(C)))))))()) 16 | 17 | 18 | if __name__ in tup(M)(): 19 | set_value(chr(99))(C(42)) 20 | print(getattr(*tup(get_value(chr(99)))(chr(120)))) 21 | 22 | set_value(chr(100))(C(69)) 23 | print(get_value(chr(99)) * get_value(chr(100))) 24 | 25 | print(getattr(*tup(get_value(chr(99)))(f(reversed(dir(C)))))) 26 | -------------------------------------------------------------------------------- /v4/src/expressions.py: -------------------------------------------------------------------------------- 1 | from util import * 2 | 3 | set_value(chr(43))(A) 4 | set_value(chr(45))(build_magic(115)(117)(98)()) 5 | set_value(chr(42))(build_magic(109)(117)(108)()) 6 | set_value(chr(47))(build_magic(116)(114)(117)(101)(100)(105)(118)()) 7 | 8 | set_value(chr(97))(42) 9 | set_value(chr(98))(69) 10 | 11 | 12 | if __name__ in tup(M)(): 13 | print(get_value(chr(97))) 14 | print(get_value(chr(98))) 15 | print() 16 | 17 | print(getattr(*tup(int)(get_value(chr(43))))(*tup(get_value(chr(97)))(get_value(chr(98))))) 18 | print(getattr(*tup(int)(get_value(chr(45))))(*tup(get_value(chr(97)))(get_value(chr(98))))) 19 | print(getattr(*tup(int)(get_value(chr(42))))(*tup(get_value(chr(97)))(get_value(chr(98))))) 20 | print(getattr(*tup(int)(get_value(chr(47))))(*tup(get_value(chr(97)))(get_value(chr(98))))) 21 | -------------------------------------------------------------------------------- /v4/src/hello_world.py: -------------------------------------------------------------------------------- 1 | from util import * 2 | 3 | if __name__ in tup(M)(): 4 | print(build_string(72)(101)(108)(108)(111)(32)(87)(111)(114)(108)(100)(33)()) 5 | -------------------------------------------------------------------------------- /v4/src/import.py: -------------------------------------------------------------------------------- 1 | from util import * 2 | 3 | from importlib import * 4 | 5 | 6 | set_value(chr(73))(import_module(*tup(build_string(105)(109)(112)(111)(114)(116)())(chr(42)))) 7 | 8 | if __name__ in tup(M)(): 9 | getattr(*tup(get_value(chr(73)))(chr(73)))() 10 | -------------------------------------------------------------------------------- /v4/src/import/__init__.py: -------------------------------------------------------------------------------- 1 | class I: 2 | def __init__(*args): 3 | print(chr(69)) 4 | -------------------------------------------------------------------------------- /v4/src/quine.py: -------------------------------------------------------------------------------- 1 | class Q: 2 | def join(self): 3 | pass 4 | 5 | 6 | def s(t): 7 | print(getattr(*(next(reversed(dir(Q))) if j else str 8 | for j in range(2)))(*((chr(i) for i in t) if j else str() 9 | for j in range(2)))) 10 | print(t) 11 | print(chr(41)) 12 | 13 | 14 | s( 15 | (99, 108, 97, 115, 115, 32, 81, 58, 10, 32, 32, 32, 32, 100, 101, 102, 32, 106, 111, 105, 110, 40, 115, 101, 108, 102, 41, 58, 10, 32, 32, 32, 32, 32, 32, 32, 32, 112, 97, 115, 115, 10, 32, 32, 32, 32, 10, 32, 32, 32, 32, 10, 100, 101, 102, 32, 115, 40, 116, 41, 58, 10, 32, 32, 32, 32, 112, 114, 105, 110, 116, 40, 103, 101, 116, 97, 116, 116, 114, 40, 42, 40, 110, 101, 120, 116, 40, 114, 101, 118, 101, 114, 115, 101, 100, 40, 100, 105, 114, 40, 81, 41, 41, 41, 32, 105, 102, 32, 106, 32, 101, 108, 115, 101, 32, 115, 116, 114, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 102, 111, 114, 32, 106, 32, 105, 110, 32, 114, 97, 110, 103, 101, 40, 50, 41, 41, 41, 40, 42, 40, 40, 99, 104, 114, 40, 105, 41, 32, 102, 111, 114, 32, 105, 32, 105, 110, 32, 116, 41, 32, 105, 102, 32, 106, 32, 101, 108, 115, 101, 32, 115, 116, 114, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 102, 111, 114, 32, 106, 32, 105, 110, 32, 114, 97, 110, 103, 101, 40, 50, 41, 41, 41, 41, 10, 32, 32, 32, 32, 112, 114, 105, 110, 116, 40, 116, 41, 10, 32, 32, 32, 32, 112, 114, 105, 110, 116, 40, 99, 104, 114, 40, 52, 49, 41, 41, 10, 32, 32, 32, 32, 10, 32, 32, 32, 32, 10, 115, 40) 16 | ) 17 | -------------------------------------------------------------------------------- /v4/src/util/__init__.py: -------------------------------------------------------------------------------- 1 | class F: 2 | def __add__(self): 3 | pass 4 | 5 | def __getitem__(self): 6 | pass 7 | 8 | def __setitem__(self): 9 | pass 10 | 11 | 12 | def advance(num): 13 | def inner(iterator): 14 | for _ in range(num): 15 | next(iterator) 16 | return iterator 17 | 18 | return inner 19 | 20 | 21 | def f(iterable): 22 | return next(iter(iterable)) 23 | 24 | 25 | def s(iterable): 26 | return next(advance(1)(iter(iterable))) 27 | 28 | 29 | def tup(first): 30 | def inner(*second): 31 | if not second: 32 | return tuple(first for _ in range(1)) 33 | 34 | return tuple(f(second) if i else first for i in range(2)) 35 | 36 | return inner 37 | 38 | 39 | def get_value(name): 40 | return getattr(*tup(globals())(next(advance(10)(iter(dir(F))))))(name) 41 | 42 | 43 | def set_value(name): 44 | def inner(value): 45 | return getattr(*tup(globals())(next(advance(25)(iter(dir(F))))))(*tup(name)(value)) 46 | 47 | return inner 48 | 49 | 50 | set_value(chr(65))(f(dir(F))) 51 | set_value(chr(71))(next(advance(10)(iter(dir(F))))) 52 | set_value(chr(83))(next(advance(24)(iter(dir(F))))) 53 | 54 | set_value(chr(79))(range(1)) 55 | set_value(chr(84))(range(2)) 56 | 57 | 58 | def build_map(t): 59 | def mapper(func): 60 | def init(*initial): 61 | if not initial: 62 | return init(t()) 63 | 64 | def end(*final): 65 | if not final: 66 | return end(t()) 67 | 68 | def inner(*element): 69 | if not element: 70 | return getattr(*tup(t)(A))(*tup(f(initial))(f(final))) 71 | 72 | return init(getattr(*tup(t)(A))(*tup(f(initial))(func(f(element)))))(f(final)) 73 | 74 | return inner 75 | 76 | return end 77 | 78 | return init 79 | 80 | return mapper 81 | 82 | 83 | def build_iterable(t): 84 | return build_map(t)(lambda i: t(i for _ in O))()() 85 | 86 | 87 | def build_list(*_): 88 | return build_iterable(list)(*_) 89 | 90 | 91 | def build_tuple(*_): 92 | return build_iterable(tuple)(*_) 93 | 94 | 95 | def build_string(*_): 96 | return build_map(str)(chr)()()(*_) 97 | 98 | 99 | set_value(chr(68))(build_string(95)(95)()) 100 | 101 | 102 | def build_magic(*_): 103 | return build_map(str)(chr)(D)(D)(*_) 104 | 105 | 106 | set_value(chr(69))(build_magic(101)(113)()) 107 | set_value(chr(77))(build_magic(109)(97)(105)(110)()) 108 | --------------------------------------------------------------------------------