├── LICENSE ├── README.md ├── build.sh └── the_namingless_programming_language.cpp /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 | # The namingless programming language 2 | 3 | Naming is hard. Let's see how far can we go without. 4 | 5 | 6 | ## What's this? 7 | 8 | This is a programming language based on three paradigms: 9 | 10 | - [Tacit programming](https://en.wikipedia.org/wiki/Tacit_programming) 11 | - [Stack-oriented programming](https://en.wikipedia.org/wiki/Stack-oriented_programming) 12 | - [Array programming](https://en.wikipedia.org/wiki/Array_programming) 13 | 14 | The main feature of the language is its avoidance of any naming of any sort. True to this maxim, the language itself doesn't have a name. "The namingless programming language" is a definition. 15 | 16 | Since there is only one such language in existence, *it doesn't need a name*. 17 | 18 | 19 | ## What it's for? 20 | 21 | Fun. Mostly fun. This is a language for recreational programming. 22 | 23 | Well, of course, you can also use it as a teaching tool to showcase tacit, stack-oriented, or array programming. Or use it for punishment in a BDSM play. I don't judge. 24 | 25 | 26 | ## How does the code in this language look like? 27 | 28 | Like this: 29 | 30 | i_^_b_H_i_cpp^_)_V_b_v_J_^_E_H_leafL_==^_)_V_H_Z_Z_^_)_V_H_I_^_E_1^_2^_#_G_Z_Z_^_E_1^_2^_#_H_$_L_-^_G_m_G_&_&_ 31 | 32 | 33 | ## Holy shit! 34 | 35 | Yes. 36 | 37 | Sorry. 38 | 39 | 40 | ## What are the main concepts? 41 | 42 | ### Data structures 43 | 44 | There is one and only one data structure. Since there is only one, *it doesn't need a name*. 45 | 46 | So the data structure is a tree of chars. Every node is either a leaf containing a char, or a branch containing a dynamic array of the data structures. The array may also be empty. An empty branch is still a branch. 47 | 48 | A string is a branch where every subbranch is a leaf. 49 | 50 | A number is a string. Yes. The language supports decimal arithmetics on strings. Go COBOL! 51 | 52 | An array is a branch where every subbranch is a string. 53 | 54 | A matrix is a branch where every subbranch is an array. 55 | 56 | Please note that none of these terms are names for anything language-specific. They are widely known data structures that are implementable in the language's data structure. 57 | 58 | ### Operations 59 | 60 | There is also the operation. Not "an operation" but "the operation" since there is only one operation in the language. Since there is only one, *it doesn't need a name* either. 61 | 62 | The operation is spelled `_` and it takes the last element from the branch before it and acts accordingly. So essentially the semantics of the operation is set by the pair of a prefix - a symbol before the operation - and the operation itself. 63 | 64 | E. g. to add 2 and 2, you need to: 65 | 66 | 1. put a char 2 as a string. The pair for that is `^` and `_`. 67 | 68 | 2^_ 69 | 70 | 2. then you need another 2 as a string. You can do the same `2^_` thing but you can also duplicate the last element in the current branch with a `H` and `_` pair. 71 | 72 | H_ 73 | 74 | 3. finally, you need to perform the addition itself. That's easy, that's `+` and `_`. 75 | 76 | +_ 77 | 78 | So the final program will look like this: 79 | 80 | 2^_H_+_ 81 | 82 | And when run, it will result in: 83 | 84 | 4 85 | 86 | ### Ok but the program you write in the namingless language should have a name right? 87 | 88 | To avoid the naming problem for the programs you write, the language uses the name of the executable as the source code. 89 | 90 | Let that sink in. 91 | 92 | You get the executable interpreter for the language and rename it with the valid code in the language. This way, the file you run doesn't have a name per se but the program itself is the name of the executable you run. 93 | 94 | So to run the scary program from above you do this: 95 | 96 | ./build.sh 97 | mv the_namingless_programming_language i_^_b_H_i_cpp^_)_V_b_v_J_^_E_H_leafL_==^_)_V_H_Z_Z_^_)_V_H_I_^_E_1^_2^_#_G_Z_Z_^_E_1^_2^_#_H_$_L_-^_G_m_G_&_&_ 98 | ./i_^_b_H_i_cpp^_)_V_b_v_J_^_E_H_leafL_==^_)_V_H_Z_Z_^_)_V_H_I_^_E_1^_2^_#_G_Z_Z_^_E_1^_2^_#_H_$_L_-^_G_m_G_&_&_ 99 | 100 | Of course, you can just copy and tweak the program you already have. In fact, that's what we normally do with Python, shell, or Perl scripts anyway. 101 | 102 | ### What operation pairs are available? 103 | 104 | Here is the full list of the operation pair prefixes. 105 | 106 | - . - exit 107 | - U - underscore 108 | - Z - slash 109 | - N - backslash 110 | - J - line break 111 | - i - dot 112 | - L - space 113 | - I - single quote 114 | - Y - double quote 115 | - ^ - elevate all the last elements of the same rank 116 | - | - put an element of the current branch on top by index 117 | - \# - remove all but the targeted by index element for the selected depth 118 | - m - replicate an item multiple times 119 | - H - duplicate the last element 120 | - X - drop the last element 121 | - G - swap the last two elements 122 | - A - elevate an empty element 123 | - $ - count 124 | - v - deelevate last element 125 | - \+ - addition 126 | - \- - subtraction 127 | - x - multiplication 128 | - z - division 129 | - = - equal? 130 | - % - numerically equal? 131 | - < - less? 132 | - \> - greater? 133 | - ( - substring? 134 | - ) - superstring? 135 | - \[ - string starts with? 136 | - \] - string ends with? 137 | - T - Boolean not 138 | - W - Boolean and 139 | - M - Boolean or 140 | - C - interpret as number if possible, 0 otherwise 141 | - & - concatenate strings 142 | - E - split a string 143 | - D - join strings 144 | - V - filter by a logical value 145 | - b - load from file 146 | - p - save to file 147 | - o - delete file 148 | - e - help 149 | 150 | By the way, the list is automatically generated from the source code by the `i_^_b_H_i_cpp^_)_V_b_v_J_^_E_H_leafL_==^_)_V_H_Z_Z_^_)_V_H_I_^_E_1^_2^_#_G_Z_Z_^_E_1^_2^_#_H_$_L_-^_G_m_G_&_&_`. 151 | 152 | 153 | ### The most important operation pair 154 | 155 | #### Exit 156 | 157 | Since the source code for the language comes from the name of the executable, and on Windows, a dot separates the file name from its extension, the dot was chosen as a prefix that stops the execution. You can also use it to separate code from comments like this: 158 | 159 | i_^_b_H_i_cpp^_)_V_b_v_J_^_E_H_leafL_==^_)_V_H_Z_Z_^_)_V_H_I_^_E_1^_2^_#_G_Z_Z_^_E_1^_2^_#_H_$_L_-^_G_m_G_&_&_._list of operation prefixes 160 | 161 | 162 | ### Escape sequences 163 | 164 | #### Underscore 165 | 166 | Since `_` is the command, you can't use it in arbitrary strings as it is. So the escape for the underscore is `U_`. 167 | 168 | "U" has been chosen since it kind of contains the underscore in its bottom part. This is the recurring pattern. A grapheme that substitutes a symbol is usually a symbol with something else. 169 | 170 | 171 | #### Slash 172 | 173 | You can't usually use slashes in the file names. And even when you can it wouldn't be a good idea. Trust me. So there is an escape pair that puts `/` into the current data structure, and it is `Z_`. 174 | 175 | Like with "U", "Z" is a slash plus two extra lines. 176 | 177 | 178 | #### Backslash 179 | 180 | By the same logic, the backslash is a `N_` pair. "\\" plus two extra lines. 181 | 182 | 183 | #### Line break 184 | 185 | "J" looks like a bit like a return sign "⏎". So the pair for the line break is `J_`. 186 | 187 | 188 | #### Dot 189 | 190 | Since a dot is used as a name-extension separator on Windows, we can use `i_` as a dot substitute just not to mess with the way file managers prefer to show stuff. 191 | 192 | 193 | #### Space 194 | 195 | Although there is nothing wrong with putting space in the file name, sometimes it makes routine UI operations like copying things harder so we can avoid spaces by `L_` substitution. 196 | 197 | "L" is a horizontal line representing the space itself, and a vertical line that serves no purpose. 198 | 199 | 200 | #### Single quote 201 | 202 | `I_` stands for the single quote. You probably wouldn't get it until you see the double-quote pair. 203 | 204 | 205 | #### Double quote 206 | 207 | `Y_` is the double quote. "I" and "Y", single and double. Does it make sense now? 208 | 209 | 210 | ### Data handling 211 | 212 | #### Elevate all the last elements of the same rank 213 | 214 | I see your confusion. This prefix doesn't have a coherent name. Well, naming *is* hard, that's why I started the whole thing to begin with. 215 | 216 | What it does *usually* is it turns a piece of string into a subbranch containing the piece of string. 217 | 218 | E. g. you want to add 12 and 23 together. In a normal language, you have literals, syntaxis, and operators, so `12 + 23` is the common way to do that. In this language, you only have a string of chars as input, so to add two numbers together, you need to segregate one from another first. 219 | 220 | When you enter `12`, you only enter char `1` followed by char `2`. Your current branch contains two leaf elements. 221 | 222 | leaf(1) leaf(2) 223 | 224 | Now you elevate all the elements of the same rank making these two into a subbranch with two leaves inside. 225 | 226 | 12^_ 227 | 228 | ...results in... 229 | 230 | branch(leaf(1) leaf(2)) 231 | 232 | Now you add two more chars: 233 | 234 | 12^_34 235 | 236 | branch(leaf(1) leaf(2)) leaf(3) leaf(4) 237 | 238 | And then you elevate the last elements until they are of the same rank resulting in this: 239 | 240 | 12^_34^_ 241 | 242 | branch(leaf(1) leaf(2)) branch(leaf(3) leaf(4)) 243 | 244 | And only now, when you have two branches with what are essentially strings inside, you can use another operation pair to add them up. 245 | 246 | 12^_34^_+_ 247 | 248 | This now results in a new branch containing the following: 249 | 250 | branch(leaf(4) leaf(6)) 251 | 252 | This outputs to a tab and `46`. You can deelevate it back with the `v_` pair, and then you'll get `46` without any tabs, but that's a completely different story. 253 | 254 | 255 | #### Put an element of the current branch on top by index 256 | 257 | This is something like `[]` in normal languages. A way to access an array. 258 | 259 | Let's say our branch is filled like this: 260 | 261 | leaf(1) leaf(2) leaf(4) leaf(8) 262 | 263 | Since we might even know how many leaves there are, we start counting indices from the end. And for... reasons, we also start from 0. 264 | 265 | So doing `2^_|_` to our subbranch picks the *third* element from the right, and copies it to the rightmost position in the branch. 266 | 267 | leaf(1) leaf(2) leaf(4) leaf(8) leaf(2) 268 | 269 | 270 | #### Remove all but the targeted by index element for the selected depth 271 | 272 | This is a way to access all the elements in a matrix, or a tensor, or a tree that lies on a certain depth, and in a certain position too. 273 | 274 | Take this program for example: 275 | 276 | 1^_2^_3^_^_4^_5^_6^_^_^_2^_2^_#_ 277 | 278 | First, we elevate 3 chars so they become strings (or in terms of the language, branches that only contain leaves). 279 | 280 | branch(leaf(1)) branch(leaf(2)) branch(leaf(3)) 281 | 282 | Then we elevate the strings, yes, all three of them, so they now constitute an array of strings (or, in terms of the language again, a branch where all the branches are the branches that only contain leaves). 283 | 284 | branch(branch(leaf(1)) branch(leaf(2)) branch(leaf(3))) 285 | 286 | Then we add three more strings and elevate them as well. 287 | 288 | branch(branch(leaf(1)) branch(leaf(2)) branch(leaf(3))) branch(branch(leaf(4)) branch(leaf(5)) branch(leaf(6))) 289 | 290 | Then we evaluate the two branches making a matrix of strings (or in terms of the language, oh boy, a branch that contains several branches with an equal number of branches that only contain leaves each). 291 | 292 | branch(branch(branch(leaf(1)) branch(leaf(2)) branch(leaf(3))) branch(branch(leaf(4)) branch(leaf(5)) branch(leaf(6)))) 293 | 294 | Or in more conventional notation: 295 | 296 | 1 2 3 297 | 4 5 6 298 | 299 | Now we do this: 300 | 301 | 2^_2^_#_ 302 | 303 | The first `2` is the index. And it also enumerates elements starting from 0, so `2` actually means "the third". 304 | 305 | The second `2` is the depth. Also starting from 0. `2` and `2` means that we want the third element from the branch on the third level of embededness. That's `leaf(3)` and `leaf(6)` but also packed in a branch so it's more like `branch(leaf(3) leaf(6))`. 306 | 307 | This operation prefix may also pick from the 0-th deepness level so, for instance, this program: 308 | 309 | 1^_2^_3^_2^_0^_#_ 310 | 311 | Results in `3`. 312 | 313 | 314 | #### Replicate an item multiple times 315 | 316 | Well, that's easy. If there is an element in the current branch, it can be multiplicated. For example, `3` times by doing `3^_m_`: 317 | 318 | test^_3^_m_ 319 | 320 | results in: 321 | 322 | test 323 | test 324 | test 325 | 326 | 327 | #### Duplicate the last element 328 | 329 | This is the abbreviated version of replication. It always adds exactly 1 copy of the last element. 330 | 331 | test^_H_ 332 | 333 | results in: 334 | 335 | test 336 | test 337 | 338 | 339 | #### Drop the last element 340 | 341 | The last element can also be easily removed. 342 | 343 | 1^_2^_3^_X_ 344 | 345 | results in: 346 | 347 | 1 348 | 2 349 | 350 | 351 | #### Swap the last two elements 352 | 353 | Or swapped with the previous element. 354 | 355 | 1^_2^_3^_G_ 356 | 357 | results in: 358 | 359 | 1 360 | 3 361 | 2 362 | 363 | 364 | #### Elevate an empty element 365 | 366 | Sometimes you need an empty string, an empty array, or an empty matrix. You can't elevate an empty element with `^_` because it elevates all the consequent elements of the same rank starting from the last one. So there is a dedicated prefix that elevates an empty element out of the blue. 367 | 368 | A_ 369 | 370 | results in 371 | 372 | branch() 373 | 374 | 375 | #### Count 376 | 377 | This simply returns the amount of subbranches in the last element of the current branch. 378 | 379 | 1^_2^_3^_^_$_ 380 | 381 | results in: 382 | 383 | 3 384 | 385 | 386 | #### Deelevate the last element 387 | 388 | Just like several subbranches can be elevated into one branch containing them all, the last element in the branch can be deelevated, putting all its contents in the current branch. 389 | 390 | 1^_2^_3^_^_v_ 391 | 392 | results, unsurprisingly, in: 393 | 394 | 1 395 | 2 396 | 3 397 | 398 | The symbol pairs the grapheme for the "elevate" by the way. Isn't that sweet? 399 | 400 | 401 | ### Arithmetics 402 | 403 | I love floating-point numbers. I wrote several tutorials on them: 404 | 405 | -[Yet another floating-point tutorial](https://wordsandbuttons.online/yet_another_floating_point_tutorial.html) 406 | 407 | -[Estimating floating-point error the easy way](https://wordsandbuttons.online/estimating_floating_point_error_th-e_easy_way.html) 408 | 409 | -[Why is it ok to divide by 0.0? ](https://wordsandbuttons.online/why_is_it_ok_to_divide_by_0_0.html) 410 | 411 | I even wrote a whole book on [Geometry for Programmers](https://www.amazon.com/Geometry-Programmers-Oleksandr-Kaleniuk/dp/1633439607) and it's half symbolic computations, half numeric so at least half of the book is concerned with floating-point numbers too. 412 | 413 | But for this particular language, I wanted to avoid introducing another type so much, that I do all the arithmetics on decimal strings. Like in COBOL. 414 | 415 | There is one unorthodox rule: the precision of the result is the maximum precision of the argument. The rule means that `1/3` and `1.00/3` result in different numbers. The former is `0`, and the latter - `0.33`. The rest is business as usual. 416 | 417 | Except, of course, this time the expressions are in postfix notation, they propagate to tensors, and instead of operators we have prefix+operation pairs. 418 | 419 | 420 | #### Addition 421 | 422 | The prefix for addition is `+`. 423 | 424 | 2^_2^_+_ 425 | 426 | 4 427 | 428 | In case you're wondering what the "they propagate to tensors" statement from above means, that means that you can add a number to an array: 429 | 430 | 1^_2^_^_3^_+_ 431 | 432 | 4 433 | 5 434 | 435 | Or add elements in a pair of arrays together: 436 | 437 | 1^_2^_^_3^_4^_^_+_ 438 | 439 | 4 440 | 6 441 | 442 | You can also add matrices or even arbitrary trees as long as they are of equal rank, size, and configuration. All with a single operation. You don't need `for`s in this language. 443 | 444 | 445 | #### Subtraction 446 | 447 | The prefix for subtraction is `-`. 448 | 449 | 4^_2^_-_ 450 | 451 | 2 452 | 453 | 454 | #### Multiplication 455 | 456 | Since we wouldn't use `*` for multiplication, remember, that the source code comes from the file name, the prefix for the multiplication would be `x`. 457 | 458 | 2^_2^_x_ 459 | 460 | 4 461 | 462 | 463 | #### Division 464 | 465 | Just like that, we wouldn't use `/` for division either. The prefix for the division is `z`. 466 | 467 | 4^_2^_z_ 468 | 469 | 4 470 | 471 | 472 | ### Logic 473 | 474 | Logical operations in this language work pretty much like arithmetics. They work on strings and propagate to tensors and trees. It's just the result of a comparison cast on a pair of trees is a tree of `0` and `1`s. 475 | 476 | 477 | #### Equal? 478 | 479 | This compares two strings. Strings may or may not be numbers. 480 | 481 | 2^_2^_=_ 482 | 483 | 1 484 | 485 | 486 | #### Numerically equal? 487 | 488 | This compares two strings. Strings are expected to be numbers but they may have trailing zeroes. 489 | 490 | 2^_2.00^_=_ 491 | 492 | 1 493 | 494 | 495 | #### Less? 496 | 497 | Expects numbers. Returns `1` if the former is less than the latter. `0` otherwise: 498 | 499 | 2^_3^_<_ 500 | 501 | 1 502 | 503 | 504 | #### Greater? 505 | 506 | Also expects numbers. Returns `1` if the former is greater than the latter. `0` otherwise: 507 | 508 | 3^_2^_>_ 509 | 510 | 1 511 | 512 | 513 | #### Substring? 514 | 515 | Expects strings. Returns `1` if the former is a substring of the latter. `0` otherwise: 516 | 517 | bob^_notabobbutcontainsone^_(_ 518 | 519 | 1 520 | 521 | 522 | #### Superstring? 523 | 524 | Symmetrical to the "substring?" operation pair. Returns `1` if the former is a superstring of the latter. `0` otherwise: 525 | 526 | notabobbutcontainsone^_bob^_)_ 527 | 528 | 1 529 | 530 | 531 | #### String starts with? 532 | 533 | Expects strings. Returns `1` if the latter is what a former starts with. `0` otherwise: 534 | 535 | bobbutcontainsone^_bob^_\[_ 536 | 537 | 1 538 | 539 | 540 | #### String ends with? 541 | 542 | Expects strings. Returns `1` if the latter is what a former finishes with. `0` otherwise: 543 | 544 | notabob^_bob^_\]_ 545 | 546 | 1 547 | 548 | 549 | #### Boolean not 550 | 551 | Expects a string of `0` and `1` only. Inverts the values in a tree. 552 | 553 | 1^_T_ 554 | 555 | 0 556 | 557 | 558 | #### Boolean and 559 | 560 | Also expects strings of `0` and `1` only. Does the Boolean "and". 561 | 562 | 1^_1^_W_ 563 | 564 | 1 565 | 566 | 567 | #### Boolean or 568 | 569 | You guessed it, also expects strings of `0` and `1` only. Does the Boolean "or". 570 | 571 | 0^_1^_M_ 572 | 573 | 1 574 | 575 | 576 | ### Strings 577 | 578 | 579 | #### Interpret as a number if possible, 0 otherwise 580 | 581 | In this language, numbers are strings that can be interpreted as numbers. So ten digits, one optional dot, and an optional minus sign. Some strings are obviously not numbers but we might wish they were. For this scenario, a special prefix exists and it's `C`. It turns all the non-numbers into `0`, 582 | 583 | 123^_not123^_^_C_ 584 | 585 | 123 586 | 0 587 | 588 | 589 | #### Concatenate strings 590 | 591 | Very simple operation that glues two strings together. 592 | 593 | 2^_2^_&_ 594 | 595 | 22 596 | 597 | 598 | #### Split a string 599 | 600 | Splits a string by a delimiter (also a string). 601 | 602 | pre,the,post^_,^_E_ 603 | 604 | pre 605 | the 606 | post 607 | 608 | 609 | #### Join strings 610 | 611 | Joins string back into a single string with a delimiter. 612 | 613 | pre^_the^_post^_^_-^D_ 614 | 615 | pre-the-post 616 | 617 | 618 | #### Replace 619 | 620 | Got you! There is no "replace a string with a string" operation pair in the language. To do the replace, you do a split-then-join idiom. 621 | 622 | pre,the,post^_,^_E_-^_D_ 623 | 624 | pre-the-post 625 | 626 | One symbol spared for something else. 627 | 628 | 629 | ### Filter 630 | 631 | There is only one filter operation so far, and it is: 632 | 633 | 634 | #### Filter by a logical value 635 | 636 | It expects two trees of the same configuration. One with strings the other with, well, also strings but only `1`s and `0`s. The filter goes through the first tree and removes every element that is not `1` in the corresponding tree. 637 | 638 | pre,the,post^_,^_E_H_p^_)_V_ 639 | 640 | So this program 641 | 642 | 1. elevates a string "pre,the,post", 643 | 2. splits it by the `,` getting an array `pre the post`, 644 | 3. duplicates the split string `pre the post`, 645 | 4. applies the "is a superstring of `p`" for the last split tree resulting in array `1 0 1`. 646 | 5. and, finally, applies the filter `1 0 1` to the `pre the post` array resulting in a smaller array with strings where the letter `p` occurs: 647 | 648 | pre 649 | post 650 | 651 | 652 | ### Files 653 | 654 | In this language, a grapheme for a file is a small circle `o`. There are exactly 3 operation prefixes that work with files, all of them have a small circle in them. 655 | 656 | 657 | #### Load from file 658 | 659 | The grapheme is `b` so it's like a file goes up from slow persistent memory to the fast operational. 660 | 661 | somefilei_txt^_b_ 662 | 663 | results in... whatever there is in the `somefile.txt`. The result will be delivered as a string - a branch where every subbranch is a leaf containing a character value. 664 | 665 | Also, this operation pair doubles as a 'list directory' command. If the argument for the `b_` is a directory name, then the result will be not a string but a list of strings each containing a file or a directory name. 666 | 667 | 668 | #### Save to file 669 | 670 | The grapheme is inverse to the one for the "load from file" it's `p`. So a file going down closer to earth. 671 | 672 | somethingL_toL_putL_inL_aL_file^_somefilei_txt^_p_ 673 | 674 | results in a string "something to put in a file" being put in a file "somefile.txt" 675 | 676 | 677 | #### Delete a file 678 | 679 | The grapheme now is just a circle `o`. Well, it might not make too much sense but it shows that the operation is a pure side effect with no effect on the current branch. Well, except for the file name being consumed. 680 | 681 | somefilei_txt^_o_ 682 | 683 | deletes the "somefile.txt" file if there is one. 684 | 685 | 686 | ### Help 687 | 688 | Last but not least, the grapheme for the help message explaining what the hell this thing does is `e`. The reasoning for this choice is simple. When you build the thing, the resulting file name is `the_namingless_programming_language`. So when you run it as it is, the first operation pair the interpreter meets is `e_` from `the_...`. 689 | 690 | 691 | ### Ok, I get it now. I love it! But there is not much the interpreter can do at the moment. Can I contribute? 692 | 693 | Sure! To add an operation pair to the language you might want to do three things. 694 | 695 | 1. Invent a meaningful program that wouldn't run unless you add your thing. 696 | 2. Add your thing so the program will run and be meaningful. 697 | 3. Prepare the pull request with the changes in the code and your program added to the list of meaningful programs. 698 | 699 | I'll start. 700 | 701 | 702 | ### The list of meaningful programs 703 | 704 | #### Parse the source code and list the operation pairs that are currently in the language 705 | 706 | i_^_b_H_i_cpp^_)_V_b_v_J_^_E_H_leafL_==^_)_V_H_Z_Z_^_)_V_H_I_^_E_1^_2^_#_G_Z_Z_^_E_1^_2^_#_H_$_L_-^_G_m_G_&_&_ 707 | 708 | This program parses the `.cpp` files in the current directory looking for a string like this: 709 | 710 | } else if(right.branches.back().leaf == 'e') { // help 711 | 712 | Such strings are then processed to extract the operation prefix, `e` in the example, and the comment, ` help` in the example. 713 | 714 | The prefixes and comments are then glued together with a dash ` -`. 715 | 716 | The resulting array is the list of operation prefixes this language supports with brief explanations of them taken from the comments: 717 | 718 | . - exit 719 | U - underscore 720 | Z - slash 721 | N - backslash 722 | J - line break 723 | i - dot 724 | L - space 725 | I - single quote 726 | Y - double quote 727 | ^ - elevate all the last elements of the same rank 728 | | - put an element of the current branch on top by index 729 | \# - remove all but the targeted by index element for the selected depth 730 | m - replicate an item multiple times 731 | H - duplicate the last element 732 | X - drop the last element 733 | G - swap the last two elements 734 | A - elevate an empty element 735 | $ - count 736 | v - deelevate last element 737 | \+ - addition 738 | \- - subtraction 739 | x - multiplication 740 | z - division 741 | = - equal? 742 | % - numerically equal? 743 | < - less? 744 | \> - greater? 745 | ( - substring? 746 | ) - superstring? 747 | \[ - string starts with? 748 | \] - string ends with? 749 | T - Boolean not 750 | W - Boolean and 751 | M - Boolean or 752 | C - interpret as number if possible, 0 otherwise 753 | & - concatenate strings 754 | E - split a string 755 | D - join strings 756 | V - filter by a logical value 757 | b - load from file 758 | p - save to file 759 | o - delete file 760 | e - help 761 | 762 | ## P. S. 763 | 764 | Not so long ago I published this namingless programming language to get it out of my system and it was a great success. I mean, I did get it out of my system successfully, and I somehow don't feel like playing with it anymore. Which is a shame since it really deserves a couple of new features. 765 | 766 | First of all, the language lacks operators. In a mathematical sense, so functions that work on functions. It would be really simple to add one since the input for an interpreter is a string, and a branch with 1-char leaves is a string itself. 767 | 768 | So the very first operator I would have added would have been the thing that does iterations. It takes a string as an argument, and evaluates the string continuously until the last argument in the subbranch is `1`. 769 | 770 | We can normally avoid doing loops and ifs with filtering, but an iteration is something that implies side effects or a consequent state change, you can't run iterations in parallel, they should run consequently. Currently, the language has no facilities for that. 771 | 772 | Don't know what a fitting grapheme should be. I would have probably taken `G` from the element swap and gave that the `S` instead. 773 | 774 | Also, I've heard that the language is not entirely namingless as prefixes work like names, so I want to challenge that a little too. A one-char prefix is just one possible way to guide the operation. There can just as well be multiple-char prefixes and, of course, one and only one no-char one. 775 | 776 | So the other feature I would have added would be a prefixless rank operation. If the prefix of the operation is not a leaf with a single char, then the operation treats the prefix as a tensor and returns its rank. 777 | 778 | I wish I could add these two features myself, they would have improved the language quite a bit, but I don't want to. It was really fun working on an esoteric language for a while, but I think as for today, I burned out. However, if you feel like picking the baton, please do. 779 | 780 | I promise it will be fun. For a while. 781 | 782 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | rm the_namingless_programming_language 2 | c++ --std=c++17 -lstdc++fs -Wno-psabi -Os -o the_namingless_programming_language the_namingless_programming_language.cpp -lstdc++fs 3 | ./the_namingless_programming_language -------------------------------------------------------------------------------- /the_namingless_programming_language.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace std; 14 | 15 | namespace Strings { 16 | // split 17 | vector split(const string& Line, const string& Coma){ 18 | vector ret; 19 | size_t coma_pos = 0; 20 | size_t coma_len = Coma.length(); 21 | while(coma_pos != string::npos){ 22 | size_t new_coma_pos = Line.find(Coma, coma_pos); 23 | if(new_coma_pos != string::npos){ 24 | ret.push_back(Line.substr(coma_pos, new_coma_pos-coma_pos)); 25 | coma_pos = new_coma_pos+coma_len; 26 | }else{ 27 | ret.push_back(Line.substr(coma_pos, string::npos)); 28 | coma_pos = string::npos; 29 | } 30 | } 31 | return ret; 32 | } 33 | 34 | // join 35 | struct join_with{ 36 | string operator()(const string& S1, const string& S2){ 37 | return (S1 + the_) + S2; 38 | } 39 | explicit join_with(const string& the) : the_(the){} 40 | private: 41 | string the_; 42 | }; 43 | 44 | string join(const vector& Lines, const string& Coma){ 45 | string ret; 46 | if(!Lines.empty()){ 47 | join_with joiner = join_with(Coma); 48 | ret = Lines.front() + accumulate(Lines.begin()+1, Lines.end(), string(), joiner); 49 | return ret; 50 | } 51 | return ret; 52 | } 53 | 54 | // replace 55 | string replace(const string& Line, const string& Coma1, const string& Coma2){ 56 | return join(split(Line, Coma1), Coma2); 57 | } 58 | } 59 | 60 | namespace Numbers { 61 | // unsigned calc routines 62 | string ensure_point(const string& S){ 63 | if(S.find_first_of(',') == string::npos){ 64 | return S+','; 65 | }else{ 66 | return S; 67 | } 68 | } 69 | 70 | string ensure_minus(const string& S){ 71 | if(S[0] != '-'){ 72 | return "-"+S; 73 | }else{ 74 | return S; 75 | } 76 | } 77 | 78 | string ensure_zero(const string& S){ 79 | if(S == "-0"){ 80 | return "0"; 81 | }else{ 82 | return S; 83 | } 84 | } 85 | 86 | string invert(const string& S){ 87 | if(S[0] != '-'){ 88 | return "-"+S; 89 | }else{ 90 | return S.substr(1, string::npos); 91 | } 92 | } 93 | 94 | void align_to_each_other_with_0(const string& S1, const string& S2, string& NS1, string& NS2){ 95 | size_t PL1 = S1.find_first_of(','); 96 | size_t PR1 = S1.length() - PL1; 97 | size_t PL2 = S2.find_first_of(','); 98 | size_t PR2 = S2.length() - PL2; 99 | size_t PLM = max(PL1, PL2); 100 | size_t PRM = max(PR1, PR2); 101 | 102 | NS1.assign(PLM+PRM+1, '0'); 103 | NS1.replace(PLM-PL1+1, S1.length(), S1); 104 | NS2.assign(PLM+PRM+1, '0'); 105 | NS2.replace(PLM-PL2+1, S2.length(), S2); 106 | } 107 | 108 | string trim_left(const string& S){ 109 | string t = S.substr(S.find_first_not_of('0'), string::npos); 110 | if(t == ""){ 111 | return "0"; 112 | } 113 | if(t[0] == ','){ 114 | return "0"+t; 115 | } 116 | return t; 117 | } 118 | 119 | string trim_right(const string& S){ 120 | string t = S.substr(0, S.find_last_not_of('0') + 1); 121 | if(t[t.length()-1] == ','){ 122 | return t.substr(0, t.length()-1); 123 | } 124 | return t; 125 | } 126 | 127 | string trim(const string& S){ 128 | string s_or_null = trim_right(trim_left(S)); 129 | if(s_or_null == ""){ 130 | return "0"; 131 | }else{ 132 | return s_or_null; 133 | } 134 | } 135 | 136 | string us_add(const string& S1, const string& S2){ 137 | string EPS1 = ensure_point(S1); 138 | string EPS2 = ensure_point(S2); 139 | 140 | string AS1, AS2; 141 | align_to_each_other_with_0(EPS1, EPS2, AS1, AS2); 142 | 143 | deque Res; 144 | char Add = 0; 145 | 146 | string::reverse_iterator It1 = AS1.rbegin(); 147 | string::reverse_iterator It2 = AS2.rbegin(); 148 | string::reverse_iterator ItE1 = AS1.rend(); 149 | for(; It1 != ItE1; ++It1, ++It2){ 150 | if(*It1 == ','){ 151 | Res.push_front(','); 152 | }else{ 153 | char n = (*It1 -'0') + (*It2 -'0') + Add; 154 | if(n > 9){ 155 | n -= 10; 156 | Add = 1; 157 | }else{ 158 | Add = 0; 159 | } 160 | Res.push_front(n + '0'); 161 | } 162 | } 163 | return trim( string(Res.begin(), Res.end()) ); 164 | } 165 | 166 | string us_sub(const string& S1, const string& S2){ 167 | string EPS1 = ensure_point(S1); 168 | string EPS2 = ensure_point(S2); 169 | string AS1, AS2, Sign; 170 | align_to_each_other_with_0(EPS1, EPS2, AS1, AS2); 171 | if(AS1.compare(AS2) < 0){ 172 | AS1.swap(AS2); 173 | Sign = "-"; 174 | }else{ 175 | Sign = ""; 176 | } 177 | 178 | deque Res; 179 | char Sub = 0; 180 | 181 | string::reverse_iterator It1 = AS1.rbegin(); 182 | string::reverse_iterator It2 = AS2.rbegin(); 183 | string::reverse_iterator ItE1 = AS1.rend(); 184 | for(; It1 != ItE1; ++It1, ++It2){ 185 | if(*It1 == ','){ 186 | Res.push_front(','); 187 | }else{ 188 | char n = 10 + (*It1 -'0') - (*It2 -'0') - Sub; 189 | if(n > 9){ 190 | n -= 10; 191 | Sub = 0; 192 | }else{ 193 | Sub = 1; 194 | } 195 | Res.push_front(n + '0'); 196 | } 197 | } 198 | return Sign + trim( string(Res.begin(), Res.end()) ); 199 | } 200 | 201 | string us_mul1(const string& S, char C){ 202 | char c = C - '0'; 203 | string EPS = ensure_point(S); 204 | string AS = "0"+EPS; 205 | 206 | deque Res; 207 | char Add = 0; 208 | 209 | string::reverse_iterator It = AS.rbegin(); 210 | string::reverse_iterator ItE = AS.rend(); 211 | for(; It != ItE; ++It){ 212 | if(*It == ','){ 213 | Res.push_front(','); 214 | }else{ 215 | char n = (*It -'0') * c + Add; 216 | Add = n / 10; 217 | n = n % 10; 218 | Res.push_front(n + '0'); 219 | } 220 | } 221 | return trim_right(string(Res.begin(), Res.end())); 222 | } 223 | 224 | string mul_10(const string& N){ 225 | size_t point = N.find_first_of(','); 226 | if(point == string::npos){ 227 | return N+"0"; 228 | }else{ 229 | string M=N; 230 | M[point] = M[point+1]; 231 | M[point+1] = ','; 232 | return trim_right(M); 233 | } 234 | } 235 | 236 | string mul_10(const string& N, unsigned int P){ 237 | if(P == 0){ 238 | return N; 239 | }else{ 240 | return mul_10(mul_10(N), P-1); 241 | } 242 | } 243 | 244 | string div_10(const string& N){ 245 | size_t point = N.find_first_of(','); 246 | string M; 247 | if(point == string::npos){ 248 | M = N + N[N.length()-1]; 249 | M[N.length()-1] = ','; 250 | }else{ 251 | M = N; 252 | M[point] = M[point-1]; 253 | M[point-1] = ','; 254 | } 255 | 256 | if(M[0]==','){ 257 | return "0"+M; 258 | }else{ 259 | return M; 260 | } 261 | } 262 | 263 | string div_10(const string& N, unsigned int P){ 264 | if(P == 0){ 265 | return N; 266 | }else{ 267 | return div_10(div_10(N), P-1); 268 | } 269 | } 270 | 271 | string us_mul(const string& S1, const string& S2){ 272 | if(S1 == "0" || S2 == "0"){ 273 | return "0"; 274 | } 275 | if(S1.length() < S2.length()){ 276 | return us_mul(S2, S1); 277 | }else{ 278 | size_t point = S2.find_first_of(','); 279 | unsigned int shift; 280 | if(point == string::npos){ 281 | shift = 0; 282 | }else{ 283 | shift = S2.length() - point -1; 284 | } 285 | string CS2 = Strings::replace(S2, ".", ""); 286 | string::reverse_iterator It = CS2.rbegin(); 287 | string::reverse_iterator ItE = CS2.rend(); 288 | vector to_sum; 289 | to_sum.reserve(CS2.length()); 290 | for(unsigned int i = 0; It != ItE; ++It, ++i){ 291 | to_sum.push_back( mul_10( us_mul1(S1, *It), i)); 292 | } 293 | return div_10( accumulate(to_sum.begin(), to_sum.end(), string("0"), us_add), shift); 294 | } 295 | } 296 | 297 | bool int_less(const string& S1, const string& S2){ 298 | if(S1.length() < S2.length())return true; 299 | if(S1.length() > S2.length())return false; 300 | if(S1.compare(S2) < 0)return true; 301 | return false; 302 | } 303 | 304 | string int_div(const string& S1, const string& S2){ 305 | if(int_less(S1, S2)){ 306 | return "0"; 307 | } 308 | 309 | string dmuls[10]; 310 | dmuls[0] = "0"; 311 | for(unsigned int i = 1; i <= 9; i++){ 312 | dmuls[i] = us_mul1(S2, '0' + i); 313 | } 314 | 315 | string n = S1; 316 | string res; 317 | size_t l1 = S1.length(); 318 | size_t l2 = S2.length(); 319 | for(int i = l1-l2; i >= 0; i--){ 320 | int j = 9; 321 | string to_subtract = "0"; 322 | for(; j > 0 ; j--){ 323 | to_subtract = trim_left(mul_10(dmuls[j], i)); 324 | if(!int_less(n, to_subtract)){ 325 | break; 326 | } 327 | } 328 | if(j>0){ 329 | n = us_sub(n, to_subtract); 330 | } 331 | res += ('0'+j); 332 | } 333 | return trim_left(res); 334 | } 335 | 336 | string us_div(const string& S1, const string& S2){ 337 | size_t point1 = S1.find_first_of(','); 338 | unsigned int after_point1 = (point1 == string::npos)? 0 : S1.length()-point1-1; 339 | size_t point2 = S2.find_first_of(','); 340 | unsigned int after_point2 = (point2 == string::npos)? 0 : S2.length()-point2-1; 341 | unsigned int max_after_point = max(after_point1, after_point2); 342 | string IS1 = mul_10(S1, max_after_point*2); 343 | string IS2 = mul_10(S2, max_after_point); 344 | return div_10( int_div(IS1, IS2), max_after_point); 345 | } 346 | 347 | 348 | // add sub mul div 349 | string add(const string& S1, const string& S2){ 350 | if(S1 == "" || S2 == ""){ 351 | cerr << ("Bad arguments: for addition \"" + S1 + "\" + \"" + S2 + "\"\n"); 352 | } 353 | bool SN1 = S1[0] == '-'; 354 | string US1 = SN1 ? S1.substr(1, string::npos) : S1; 355 | bool SN2 = S2[0] == '-'; 356 | string US2 = SN2 ? S2.substr(1, string::npos) : S2; 357 | if( !SN1 && !SN2 ){ 358 | return us_add(US1, US2); 359 | }else if( SN1 && !SN2 ){ 360 | return us_sub(US2, US1); 361 | }else if( !SN1 && SN2 ){ 362 | return us_sub(US1, US2); 363 | }else{ 364 | return ensure_minus( us_add(US1, US2) ); 365 | } 366 | } 367 | 368 | string sub(const string& S1, const string& S2){ 369 | if(S1 == ""){ 370 | cerr << ("Bad arguments: for subtraction \"" + S1 + "\" - \"" + S2 + "\"\n"); 371 | } 372 | bool SN1 = S1[0] == '-'; 373 | string US1 = SN1 ? S1.substr(1, string::npos) : S1; 374 | string RS2 = (S2=="") ? "0" : S2; // unary fix 375 | bool SN2 = RS2[0] == '-'; 376 | string US2 = SN2 ? RS2.substr(1, string::npos) : RS2; 377 | if( !SN1 && !SN2 ){ 378 | return us_sub(US1, US2); 379 | }else if( SN1 && !SN2 ){ 380 | return ensure_minus( us_add(US2, US1) ); 381 | }else if( !SN1 && SN2 ){ 382 | return us_add(US1, US2); 383 | }else{ 384 | return ensure_zero( invert( us_sub(US1, US2) ) ); 385 | } 386 | } 387 | 388 | string mul(const string& S1, const string& S2){ 389 | if(S1 == "" || S2 == ""){ 390 | cerr << ("Bad arguments: for multiplication \"" + S1 + "\" * \"" + S2 + "\"\n"); 391 | } 392 | if(S1 == "0" || S2 == "0"){ 393 | return "0"; 394 | } 395 | bool SN1 = S1[0] == '-'; 396 | string US1 = SN1 ? S1.substr(1, string::npos) : S1; 397 | bool SN2 = S2[0] == '-'; 398 | string US2 = SN2 ? S2.substr(1, string::npos) : S2; 399 | if( (!SN1 && !SN2) || (SN1 && SN2) ){ 400 | return us_mul(US1, US2); 401 | }else{ 402 | return ensure_minus( us_mul(US1, US2) ); 403 | } 404 | } 405 | 406 | string div(const string& S1, const string& S2){ 407 | if(S1 == "" || S2 == ""){ 408 | cerr << ("Bad arguments: for division \"" + S1 + "\" / \"" + S2 + "\"\n"); 409 | } 410 | if(S1 == "0"){ 411 | return "0"; 412 | } 413 | if(S2 == "0"){ 414 | cerr << ("Bad arguments: zero division in calculating: \"" + S1 + "\" / \"" + S2 + "\"\n"); 415 | } 416 | bool SN1 = S1[0] == '-'; 417 | string US1 = SN1 ? S1.substr(1, string::npos) : S1; 418 | bool SN2 = S2[0] == '-'; 419 | string US2 = SN2 ? S2.substr(1, string::npos) : S2; 420 | if( (!SN1 && !SN2) || (SN1 && SN2) ){ 421 | return us_div(US1, US2); 422 | }else{ 423 | return ensure_zero( ensure_minus( us_div(US1, US2) ) ); 424 | } 425 | } 426 | 427 | bool is_a_number(const string& S) { 428 | if(S.empty()) 429 | return false; 430 | if(S == "-") 431 | return false; 432 | if((S[0] >= '0' && S[0] <= '9') || S[0] == '-') { // starts with -, 0..9 433 | auto comas = 0u; 434 | for(size_t i = 1u; i < S.size(); ++i) { 435 | if(S[i] == ',') { 436 | comas++; 437 | if(comas > 1) 438 | return false; 439 | } else if(S[i] >= '0' && S[i] <= '9') { 440 | } else { 441 | return false; 442 | } 443 | } 444 | return true; 445 | } 446 | return false; 447 | } 448 | } 449 | 450 | struct The { 451 | bool is_leaf = true; 452 | char leaf = 0x00; 453 | vector branches; 454 | }; 455 | 456 | int rank_of(const The& could_be_a_tensor) { 457 | if(could_be_a_tensor.is_leaf) // leaf 458 | return 0; 459 | if(could_be_a_tensor.branches.empty()) // empty list 460 | return 1; 461 | return 1 + rank_of(could_be_a_tensor.branches[0]); // tensor 462 | } 463 | 464 | string to_string(const The& a, int level = 0) { 465 | if(a.is_leaf) // letter to string 466 | return string() + a.leaf; 467 | 468 | string output; 469 | 470 | if(rank_of(a) == 1) { // padding 471 | for(auto i = 0; i < level; ++i) 472 | output += '\t'; 473 | } 474 | for(const The& branch: a.branches) { // strings 475 | output += to_string(branch, level + 1); 476 | } 477 | output += '\n'; 478 | return output; 479 | } 480 | 481 | string rank_1_to_string(const The& a) { 482 | if(rank_of(a) != 1) { 483 | cerr << "Argument error: rank of the string-convertible structure should be 1\n"; 484 | return string(); 485 | } 486 | string output; 487 | for(const auto& b: a.branches) { 488 | output += b.leaf; 489 | } 490 | return output; 491 | } 492 | 493 | The string_to_rank_1(const string& s) { 494 | The a; 495 | a.is_leaf = false; 496 | a.branches.resize(s.size()); 497 | for(auto i = 0u; i < s.size(); ++i) { 498 | a.branches[i].leaf = s[i]; 499 | } 500 | return a; 501 | } 502 | 503 | vector rank_2_to_strings(const The& a) { 504 | vector output; 505 | if(rank_of(a) != 2) { 506 | cerr << "Argument error: rank of the string-array-convertible structure should be 2\n"; 507 | return output; 508 | } 509 | for(const auto& b: a.branches) { 510 | output.push_back(rank_1_to_string(b)); 511 | } 512 | return output; 513 | } 514 | 515 | The strings_to_rank_2(const vector& ss) { 516 | The a; 517 | a.is_leaf = false; 518 | a.branches.resize(ss.size()); 519 | for(auto i = 0u; i < ss.size(); ++i) { 520 | a.branches[i] = string_to_rank_1(ss[i]); 521 | } 522 | return a; 523 | } 524 | 525 | The binary_rank_to_rank(const The& left, const The& right, int left_rank, int right_rank, function f2) { 526 | if(rank_of(left) == left_rank && rank_of(right) == right_rank) { 527 | return f2(left, right); 528 | } else if(rank_of(left) == left_rank){ 529 | The a; 530 | a.is_leaf = false; 531 | for(auto b: right.branches) { 532 | a.branches.push_back(binary_rank_to_rank(left, b, left_rank, right_rank, f2)); 533 | } 534 | return a; 535 | }else if(rank_of(right) == right_rank){ 536 | The a; 537 | a.is_leaf = false; 538 | for(auto b: left.branches) { 539 | a.branches.push_back(binary_rank_to_rank(b, right, left_rank, right_rank, f2)); 540 | } 541 | return a; 542 | } else { 543 | if(left.branches.size() != right.branches.size()) { 544 | cerr << "Rank error in a binary operation: argument sizes don't match.\n"; 545 | return The(); 546 | } 547 | const auto element_count = left.branches.size(); 548 | The a; 549 | a.is_leaf = false; 550 | for(auto i = 0; i < element_count; ++i) { 551 | const auto lb = left.branches[i]; 552 | const auto rb = right.branches[i]; 553 | a.branches.push_back(binary_rank_to_rank(lb, rb, left_rank, right_rank, f2)); 554 | } 555 | return a; 556 | } 557 | } 558 | 559 | // for most operations like "+" or "split" 560 | The binary_1_to_1(const The& left, const The& right, function f2) { 561 | return binary_rank_to_rank(left, right, 1, 1, f2); 562 | } 563 | 564 | // for things like "join" 565 | The binary_2_to_1(const The& left, const The& right, function f2) { 566 | return binary_rank_to_rank(left, right, 2, 1, f2); 567 | } 568 | 569 | // exclusively V (filter) 570 | The filtered(const The& left, const The& right) { 571 | if(rank_of(left) == 2 && rank_of(right) == 2) { 572 | The result; 573 | result.is_leaf = false; 574 | if(left.branches.size() != right.branches.size()) { 575 | cerr << "Rank error in V: filter size don't match the size of the filtered array.\n"; 576 | return The(); 577 | } 578 | for(size_t i = 0u; i < left.branches.size(); ++i) { 579 | if(right.branches[i].branches.size() != 1) { 580 | cerr << "Filter error in V: filter values shoud be either '0' or '1' exclusively. A value of rank " << rank_of(right.branches[i]) << " found instead.\n"; 581 | return The(); 582 | } 583 | if(right.branches[i].branches[0].leaf == '1') { 584 | result.branches.push_back(left.branches[i]); 585 | } else if(right.branches[i].branches[0].leaf == '0') { 586 | // don't push back 587 | } else { 588 | cerr << "Filter error in V: filter values shoud be either '0' or '1' exclusively. A value of '" << right.branches[i].branches[0].leaf << "' found instead.\n"; 589 | return The(); 590 | } 591 | } 592 | return result; 593 | } else if(rank_of(left) == 2){ 594 | The a; 595 | a.is_leaf = false; 596 | for(auto b: right.branches) { 597 | a.branches.push_back(filtered(left, b)); 598 | } 599 | return a; 600 | }else if(rank_of(right) == 2){ 601 | The a; 602 | a.is_leaf = false; 603 | for(auto b: left.branches) { 604 | a.branches.push_back(filtered(b, right)); 605 | } 606 | return a; 607 | } else { 608 | The a; 609 | a.is_leaf = false; 610 | for(auto lb: left.branches) { 611 | The la; 612 | la.is_leaf = false; 613 | for(auto rb: right.branches) { 614 | la.branches.push_back(filtered(lb, rb)); 615 | } 616 | a.branches.push_back(la); 617 | } 618 | return a; 619 | } 620 | } 621 | 622 | // unary operations 623 | The unary_rank(const The& the, int rank, function f1) { 624 | if(rank_of(the) == rank){ 625 | return f1(the); 626 | } else { 627 | The a; 628 | a.is_leaf = false; 629 | for(auto b: the.branches) { 630 | a.branches.push_back(unary_rank(b, rank, f1)); 631 | } 632 | return a; 633 | } 634 | } 635 | 636 | // e.g. for file load 637 | The unary_1(const The& the, function f1) { 638 | return unary_rank(the, 1, f1); 639 | } 640 | 641 | // does all the tree consist of "Booleans"? 642 | bool invertable(const The& t) { 643 | if(t.is_leaf) { 644 | if(t.leaf == '0' || t.leaf == '1') 645 | return true; 646 | else 647 | return false; 648 | } else { 649 | for(auto& b: t.branches) 650 | if(!invertable(b)) 651 | return false; 652 | return true; 653 | } 654 | } 655 | 656 | // invert all the tree (given that it does consist of "Booleans") 657 | void invert(The& t) { 658 | if(t.is_leaf) { 659 | if(t.leaf == '1') 660 | t.leaf = '0'; 661 | else 662 | t.leaf = '1'; 663 | } else { 664 | for(auto& b: t.branches) 665 | invert(b); 666 | } 667 | } 668 | 669 | // remove all but the targeted by index element for selected depth 670 | void select_by_index_and_depth(The& the, int index, int depth) { 671 | if(depth == 0) { 672 | if(the.branches.size() <= index) { 673 | cerr << "Index error: select by index and depth found a branch with not enough branches\n"; 674 | return; 675 | } 676 | the = the.branches[index]; 677 | } 678 | for(auto& branch : the.branches) 679 | select_by_index_and_depth(branch, index, depth-1); 680 | } 681 | 682 | void tests(); 683 | 684 | // dispatch the operation 685 | void execute(The& left, The& right) { 686 | if(left.branches.empty()) 687 | return; // we're done! 688 | 689 | if(left.branches.back().is_leaf && left.branches.back().leaf == '_') { 690 | left.branches.pop_back(); 691 | if(right.branches.size() < 1) { 692 | cerr << "Syntax error: the command has 0 no arguments\n"; 693 | return; 694 | } 695 | if(!right.branches.back().is_leaf) { 696 | cerr << "Syntax error: a tensor can't be the command's argument\n"; 697 | return; 698 | } 699 | if(right.branches.back().leaf == '.') { // exit 700 | right.branches.pop_back(); 701 | return; 702 | } 703 | // non-return operations 704 | if(right.branches.back().leaf == 'U') { // underscore 705 | right.branches.back().leaf ='_'; 706 | execute(left, right); 707 | } else if(right.branches.back().leaf == 'Z') { // slash 708 | right.branches.back().leaf ='/'; 709 | execute(left, right); 710 | } else if(right.branches.back().leaf == 'N') { // backslash 711 | right.branches.back().leaf ='\\'; 712 | execute(left, right); 713 | } else if(right.branches.back().leaf == 'J') { // line break 714 | right.branches.back().leaf = '\n'; 715 | execute(left, right); 716 | } else if(right.branches.back().leaf == 'i') { // dot 717 | right.branches.back().leaf = '.'; 718 | execute(left, right); 719 | } else if(right.branches.back().leaf == 'L') { // space 720 | right.branches.back().leaf = ' '; 721 | execute(left, right); 722 | } else if(right.branches.back().leaf == 'I') { // single quote 723 | right.branches.back().leaf = '\''; 724 | execute(left, right); 725 | } else if(right.branches.back().leaf == 'Y') { // double quote 726 | right.branches.back().leaf = '\"'; 727 | execute(left, right); 728 | } else if(right.branches.back().leaf == '^') { // elevate all the last elements of the same rank 729 | right.branches.pop_back(); 730 | if(right.branches.size() < 1) { 731 | cerr << "Syntax error: ^ has nothing to elevate\n"; 732 | return; 733 | } 734 | The elevated; 735 | elevated.is_leaf = false; 736 | auto target_rank = rank_of(right.branches.back()); 737 | int index_of_first_element_with_fitting_rank = right.branches.size() - 2; 738 | while(index_of_first_element_with_fitting_rank >= 0 && rank_of(right.branches[index_of_first_element_with_fitting_rank]) == target_rank) { 739 | --index_of_first_element_with_fitting_rank; 740 | } 741 | ++index_of_first_element_with_fitting_rank; 742 | for(auto i = index_of_first_element_with_fitting_rank; i < right.branches.size(); ++i) { 743 | elevated.branches.push_back(right.branches[i]); 744 | } 745 | right.branches.erase(right.branches.begin() + index_of_first_element_with_fitting_rank, right.branches.end()); 746 | right.branches.push_back(elevated); 747 | execute(left, right); 748 | } else if(right.branches.back().leaf == '|') { // put an element of the current branch on top by index 749 | right.branches.pop_back(); // throw away the bracket 750 | if(right.branches.size() < 1) { 751 | cerr << "Syntax error: ' operation (AKA atbitrary access) has no index\n"; 752 | return; 753 | } 754 | const auto& index_container = right.branches.back(); 755 | auto index = stoi(rank_1_to_string(index_container)); 756 | right.branches.pop_back(); // throw away the index container 757 | right.branches.push_back(*(right.branches.rbegin() + index)); 758 | execute(left, right); 759 | } else if(right.branches.back().leaf == '#') { // remove all but the targeted by index element for the selected depth 760 | right.branches.pop_back(); // throw away the bracket 761 | if(right.branches.size() < 2) { 762 | cerr << "Syntax error: \" operation (AKA select by index and depth) requires both index and depth arguments\n"; 763 | return; 764 | } 765 | auto depth = stoi(rank_1_to_string(right.branches.back())); 766 | auto index = stoi(rank_1_to_string(right.branches[right.branches.size()-2])); 767 | right.branches.pop_back(); // throw away the depth container 768 | right.branches.pop_back(); // throw away the index container 769 | if(depth > 0) 770 | select_by_index_and_depth(right.branches.back(), index, depth-1); 771 | else 772 | select_by_index_and_depth(right, index, depth); 773 | execute(left, right); 774 | } else if(right.branches.back().leaf == 'm') { // replicate an item multiple times 775 | right.branches.pop_back(); 776 | if(right.branches.size() < 2) { 777 | cerr << "Syntax error: m operation (AKA replicate) need two arguments - an item to replicate and a number of replications\n"; 778 | return; 779 | } 780 | auto item = right.branches[right.branches.size() - 2]; 781 | auto n_replications = stoi(rank_1_to_string(right.branches[right.branches.size() - 1])); 782 | The replications; 783 | replications.is_leaf = false; 784 | for(auto i = 0; i < n_replications; ++i) 785 | replications.branches.push_back(item); 786 | right.branches.pop_back(); 787 | right.branches.back() = replications; 788 | execute(left, right); 789 | } else if(right.branches.back().leaf == 'H') { // duplicate the last element 790 | right.branches.pop_back(); 791 | if(right.branches.size() < 1) { 792 | cerr << "Syntax error: H operation (AKA dup) has no argument to duplicate\n"; 793 | return; 794 | } 795 | right.branches.push_back(right.branches.back()); 796 | execute(left, right); 797 | } else if(right.branches.back().leaf == 'X') { // drop the last element 798 | right.branches.pop_back(); 799 | if(right.branches.size() < 1) { 800 | cerr << "Syntax error: X operation (AKA drop) has no argument to drop\n"; 801 | return; 802 | } 803 | right.branches.pop_back(); 804 | execute(left, right); 805 | } else if(right.branches.back().leaf == 'G') { // swap the last two elements 806 | right.branches.pop_back(); 807 | if(right.branches.size() < 2) { 808 | cerr << "Syntax error: G operation (AKA swap) has not enough arguments to swap\n"; 809 | return; 810 | } 811 | const auto last_branch = right.branches.size() - 1; 812 | swap(right.branches[last_branch], right.branches[last_branch - 1]); 813 | execute(left, right); 814 | } else if(right.branches.back().leaf == 'A') { // elevate an empty element 815 | right.branches.back().is_leaf = false; 816 | right.branches.back().branches.clear(); 817 | execute(left, right); 818 | } else if(right.branches.back().leaf == '$') { // count 819 | right.branches.pop_back(); 820 | if(right.branches.size() < 1) { 821 | cerr << "Syntax error: $ operation (AKA count) has no argument to count things into\n"; 822 | return; 823 | } 824 | right.branches.back() = string_to_rank_1(to_string(right.branches.back().branches.size())); 825 | execute(left, right); 826 | } else if(right.branches.back().leaf == 'v') { // deelevate last element 827 | right.branches.pop_back(); 828 | if(right.branches.size() < 1) { 829 | cerr << "Syntax error: v operation (AKA deelevate) has no argument to deelevate\n"; 830 | return; 831 | } 832 | auto the_copy = right.branches.back(); 833 | right.branches.pop_back(); 834 | for(const auto& one_in_copy: the_copy.branches) 835 | right.branches.push_back(one_in_copy); 836 | execute(left, right); 837 | } else if(right.branches.back().leaf == '+') { // addition 838 | right.branches.pop_back(); 839 | if(right.branches.size() < 2) { 840 | cerr << "Syntax error: binary operation + requires 2 arguments\n"; 841 | return; 842 | } 843 | auto l = right.branches[right.branches.size() - 2]; 844 | auto r = right.branches[right.branches.size() - 1]; 845 | // computation goes to the left argument, the right one will be then popped 846 | right.branches[right.branches.size() - 2] = binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 847 | auto ls = rank_1_to_string(lt); 848 | auto rs = rank_1_to_string(rt); 849 | return string_to_rank_1(Numbers::add(ls, rs)); 850 | }); 851 | 852 | right.branches.pop_back(); 853 | execute(left, right); 854 | } else if(right.branches.back().leaf == '-') { // subtraction 855 | right.branches.pop_back(); 856 | if(right.branches.size() < 2) { 857 | cerr << "Syntax error: binary operation - requires 2 arguments\n"; 858 | return; 859 | } 860 | auto l = right.branches[right.branches.size() - 2]; 861 | auto r = right.branches[right.branches.size() - 1]; 862 | // computation goes to the left argument, the right one will be then popped 863 | right.branches[right.branches.size() - 2] = binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 864 | auto ls = rank_1_to_string(lt); 865 | auto rs = rank_1_to_string(rt); 866 | return string_to_rank_1(Numbers::sub(ls, rs)); 867 | }); 868 | 869 | right.branches.pop_back(); 870 | execute(left, right); 871 | } else if(right.branches.back().leaf == 'x') { // multiplication 872 | right.branches.pop_back(); 873 | if(right.branches.size() < 2) { 874 | cerr << "Syntax error: binary operation x (AKA *) requires 2 arguments\n"; 875 | return; 876 | } 877 | auto l = right.branches[right.branches.size() - 2]; 878 | auto r = right.branches[right.branches.size() - 1]; 879 | // computation goes to the left argument, the right one will be then popped 880 | right.branches[right.branches.size() - 2] = binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 881 | auto ls = rank_1_to_string(lt); 882 | auto rs = rank_1_to_string(rt); 883 | return string_to_rank_1(Numbers::mul(ls, rs)); 884 | }); 885 | 886 | right.branches.pop_back(); 887 | execute(left, right); 888 | } else if(right.branches.back().leaf == 'z') { // division 889 | right.branches.pop_back(); 890 | if(right.branches.size() < 2) { 891 | cerr << "Syntax error: binary operation z (AKA /) requires 2 arguments\n"; 892 | return; 893 | } 894 | auto l = right.branches[right.branches.size() - 2]; 895 | auto r = right.branches[right.branches.size() - 1]; 896 | // computation goes to the left argument, the right one will be then popped 897 | right.branches[right.branches.size() - 2] = binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 898 | auto ls = rank_1_to_string(lt); 899 | auto rs = rank_1_to_string(rt); 900 | return string_to_rank_1(Numbers::div(ls, rs)); 901 | }); 902 | 903 | right.branches.pop_back(); 904 | execute(left, right); 905 | } else if(right.branches.back().leaf == '=') { // equal? 906 | right.branches.pop_back(); 907 | if(right.branches.size() < 2) { 908 | cerr << "Syntax error: binary operation = (AKA equal?) requires 2 arguments\n"; 909 | return; 910 | } 911 | auto l = right.branches[right.branches.size() - 2]; 912 | auto r = right.branches[right.branches.size() - 1]; 913 | // computation goes to the left argument, the right one will be then popped 914 | right.branches[right.branches.size() - 2] = binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 915 | auto ls = rank_1_to_string(lt); 916 | auto rs = rank_1_to_string(rt); 917 | return (ls == rs) ? string_to_rank_1("1") : string_to_rank_1("0"); 918 | }); 919 | right.branches.pop_back(); 920 | execute(left, right); 921 | } else if(right.branches.back().leaf == '%') { // numerically equal? 922 | right.branches.pop_back(); 923 | if(right.branches.size() < 2) { 924 | cerr << "Syntax error: binary operation % (AKA numerical equal?) requires 2 arguments\n"; 925 | return; 926 | } 927 | auto l = right.branches[right.branches.size() - 2]; 928 | auto r = right.branches[right.branches.size() - 1]; 929 | // computation goes to the left argument, the right one will be then popped 930 | right.branches[right.branches.size() - 2] = binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 931 | auto ls = rank_1_to_string(lt); 932 | auto rs = rank_1_to_string(rt); 933 | if(Numbers::is_a_number(ls) && Numbers::is_a_number(rs)) { 934 | auto difference = Numbers::sub(ls, rs); 935 | return (difference == "0") ? string_to_rank_1("1") : string_to_rank_1("0"); 936 | } else { 937 | cerr << "Syntax error: binary operation % (AKA numerical equal?) requires 2 arguments to be number-interpretable\n"; 938 | return The(); 939 | } 940 | }); 941 | right.branches.pop_back(); 942 | execute(left, right); 943 | } else if(right.branches.back().leaf == '<') { // less? 944 | right.branches.pop_back(); 945 | if(right.branches.size() < 2) { 946 | cerr << "Syntax error: binary operation < (AKA less) requires 2 arguments\n"; 947 | return; 948 | } 949 | auto l = right.branches[right.branches.size() - 2]; 950 | auto r = right.branches[right.branches.size() - 1]; 951 | // computation goes to the left argument, the right one will be then popped 952 | right.branches[right.branches.size() - 2] = binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 953 | auto ls = rank_1_to_string(lt); 954 | auto rs = rank_1_to_string(rt); 955 | if(Numbers::is_a_number(ls) && Numbers::is_a_number(rs)) { 956 | auto difference = Numbers::sub(ls, rs); 957 | return (difference[0] == '-') ? string_to_rank_1("1") : string_to_rank_1("0"); 958 | } else { 959 | cerr << "Syntax error: binary operation < (AKA less) requires 2 arguments to be number-interpretable\n"; 960 | return The(); 961 | } 962 | }); 963 | right.branches.pop_back(); 964 | execute(left, right); 965 | } else if(right.branches.back().leaf == '>') { // greater? 966 | right.branches.pop_back(); 967 | if(right.branches.size() < 2) { 968 | cerr << "Syntax error: binary operation > (AKA greater) requires 2 arguments\n"; 969 | return; 970 | } 971 | auto l = right.branches[right.branches.size() - 2]; 972 | auto r = right.branches[right.branches.size() - 1]; 973 | // computation goes to the left argument, the right one will be then popped 974 | right.branches[right.branches.size() - 2] = binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 975 | auto ls = rank_1_to_string(lt); 976 | auto rs = rank_1_to_string(rt); 977 | if(Numbers::is_a_number(ls) && Numbers::is_a_number(rs)) { 978 | auto difference = Numbers::sub(rs, ls); 979 | return (difference[0] == '-') ? string_to_rank_1("1") : string_to_rank_1("0"); 980 | } else { 981 | cerr << "Syntax error: binary operation > (AKA greater) requires 2 arguments to be number-interpretable\n"; 982 | return The(); 983 | } 984 | }); 985 | right.branches.pop_back(); 986 | execute(left, right); 987 | } else if(right.branches.back().leaf == '(') { // substring? 988 | right.branches.pop_back(); 989 | if(right.branches.size() < 2) { 990 | cerr << "Syntax error: binary operation ( (AKA substring) requires 2 arguments\n"; 991 | return; 992 | } 993 | auto l = right.branches[right.branches.size() - 2]; 994 | auto r = right.branches[right.branches.size() - 1]; 995 | // computation goes to the left argument, the right one will be then popped 996 | right.branches[right.branches.size() - 2] = binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 997 | auto ls = rank_1_to_string(lt); 998 | auto rs = rank_1_to_string(rt); 999 | return (rs.find(ls) != std::string::npos) ? string_to_rank_1("1") : string_to_rank_1("0"); 1000 | }); 1001 | right.branches.pop_back(); 1002 | execute(left, right); 1003 | } else if(right.branches.back().leaf == ')') { // superstring? 1004 | right.branches.pop_back(); 1005 | if(right.branches.size() < 2) { 1006 | cerr << "Syntax error: binary operation ) (AKA superstring) requires 2 arguments\n"; 1007 | return; 1008 | } 1009 | auto l = right.branches[right.branches.size() - 2]; 1010 | auto r = right.branches[right.branches.size() - 1]; 1011 | // computation goes to the left argument, the right one will be then popped 1012 | right.branches[right.branches.size() - 2] = binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 1013 | auto ls = rank_1_to_string(lt); 1014 | auto rs = rank_1_to_string(rt); 1015 | return (ls.find(rs) != std::string::npos) ? string_to_rank_1("1") : string_to_rank_1("0"); 1016 | }); 1017 | right.branches.pop_back(); 1018 | execute(left, right); 1019 | } else if(right.branches.back().leaf == '[') { // string starts with? 1020 | right.branches.pop_back(); 1021 | if(right.branches.size() < 2) { 1022 | cerr << "Syntax error: binary operation [ (AKA string starts?) requires 2 arguments\n"; 1023 | return; 1024 | } 1025 | auto l = right.branches[right.branches.size() - 2]; 1026 | auto r = right.branches[right.branches.size() - 1]; 1027 | right.branches[right.branches.size() - 2] = binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 1028 | auto ls = rank_1_to_string(lt); 1029 | auto rs = rank_1_to_string(rt); 1030 | return (ls.find(rs) == 0) ? string_to_rank_1("1") : string_to_rank_1("0"); 1031 | }); 1032 | right.branches.pop_back(); 1033 | execute(left, right); 1034 | } else if(right.branches.back().leaf == ']') { // string ends with? 1035 | right.branches.pop_back(); 1036 | if(right.branches.size() < 2) { 1037 | cerr << "Syntax error: binary operation ] (AKA string ends?) requires 2 arguments\n"; 1038 | return; 1039 | } 1040 | auto l = right.branches[right.branches.size() - 2]; 1041 | auto r = right.branches[right.branches.size() - 1]; 1042 | right.branches[right.branches.size() - 2] = binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 1043 | auto ls = rank_1_to_string(lt); 1044 | auto rs = rank_1_to_string(rt); 1045 | if (ls.length() >= rs.length()) { 1046 | return (ls.compare(ls.length() - rs.length(), rs.length(), rs) == 0) ? string_to_rank_1("1") : string_to_rank_1("0"); 1047 | } else { 1048 | return string_to_rank_1("0"); 1049 | } 1050 | }); 1051 | right.branches.pop_back(); 1052 | execute(left, right); 1053 | } else if(right.branches.back().leaf == 'T') { // Boolean not 1054 | right.branches.pop_back(); 1055 | if(right.branches.size() < 1) { 1056 | cerr << "Syntax error: binary operation T (AKA Boolean not) requires 1 argument\n"; 1057 | return; 1058 | } 1059 | if(!invertable(right.branches[right.branches.size() - 1])) { 1060 | cerr << "Syntax error: binary operation T (AKA Boolean not) found a value which is not 1 neither 0\n"; 1061 | return; 1062 | } 1063 | invert(right.branches[right.branches.size() - 1]); 1064 | execute(left, right); 1065 | } else if(right.branches.back().leaf == 'W') { // Boolean and 1066 | right.branches.pop_back(); 1067 | if(right.branches.size() < 2) { 1068 | cerr << "Syntax error: binary operation W (AKA Boolean and) requires 2 arguments\n"; 1069 | return; 1070 | } 1071 | if(!invertable(right.branches[right.branches.size() - 1]) || !invertable(right.branches[right.branches.size() - 2])) { 1072 | cerr << "Syntax error: binary operation W (AKA Boolean and) found a value which is not 1 neither 0\n"; 1073 | return; 1074 | } 1075 | auto l = right.branches[right.branches.size() - 2]; 1076 | auto r = right.branches[right.branches.size() - 1]; 1077 | right.branches[right.branches.size() - 2] = binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 1078 | auto ls = rank_1_to_string(lt); 1079 | auto rs = rank_1_to_string(rt); 1080 | if(ls == "1" && rs == "1") 1081 | return string_to_rank_1("1"); 1082 | else 1083 | return string_to_rank_1("0"); 1084 | }); 1085 | right.branches.pop_back(); 1086 | execute(left, right); 1087 | } else if(right.branches.back().leaf == 'M') { // Boolean or 1088 | right.branches.pop_back(); 1089 | if(right.branches.size() < 2) { 1090 | cerr << "Syntax error: binary operation M (AKA Boolean or) requires 2 arguments\n"; 1091 | return; 1092 | } 1093 | if(!invertable(right.branches[right.branches.size() - 1]) || !invertable(right.branches[right.branches.size() - 2])) { 1094 | cerr << "Syntax error: binary operation M (AKA Boolean or) found a value which is not 1 neither 0\n"; 1095 | return; 1096 | } 1097 | auto l = right.branches[right.branches.size() - 2]; 1098 | auto r = right.branches[right.branches.size() - 1]; 1099 | right.branches[right.branches.size() - 2] = binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 1100 | auto ls = rank_1_to_string(lt); 1101 | auto rs = rank_1_to_string(rt); 1102 | if(ls == "0" && rs == "0") 1103 | return string_to_rank_1("0"); 1104 | else 1105 | return string_to_rank_1("1"); 1106 | }); 1107 | right.branches.pop_back(); 1108 | execute(left, right); 1109 | } else if(right.branches.back().leaf == 'C') { // interpret as number if possible, 0 otherwise 1110 | right.branches.pop_back(); 1111 | if(right.branches.size() < 1) { 1112 | cerr << "Syntax error: # operation (AKA interpret as number) has no argument to interpret\n"; 1113 | return; 1114 | } 1115 | right.branches.back() = unary_1(right.branches.back(), [](const The& the) -> The{ 1116 | auto value = rank_1_to_string(the); 1117 | if(Numbers::is_a_number(value)) { 1118 | // good! 1119 | return the; 1120 | } else { 1121 | // also good 1122 | return string_to_rank_1(string("0")); 1123 | } 1124 | }); 1125 | execute(left, right); 1126 | } else if(right.branches.back().leaf == '&') { // concatenate strings 1127 | right.branches.pop_back(); 1128 | if(right.branches.size() < 2) { 1129 | cerr << "Syntax error: binary operation & (AKA concatenation) requires 2 arguments\n"; 1130 | return; 1131 | } 1132 | auto l = right.branches[right.branches.size() - 2]; 1133 | auto r = right.branches[right.branches.size() - 1]; 1134 | right.branches[right.branches.size() - 2] = binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 1135 | auto ls = rank_1_to_string(lt); 1136 | auto rs = rank_1_to_string(rt); 1137 | return string_to_rank_1(ls + rs); 1138 | }); 1139 | 1140 | right.branches.pop_back(); 1141 | execute(left, right); 1142 | } else if(right.branches.back().leaf == 'E') { // split a string 1143 | right.branches.pop_back(); 1144 | if(right.branches.size() < 2) { 1145 | cerr << "Syntax error: binary operation E (AKA split) requires 2 arguments\n"; 1146 | return; 1147 | } 1148 | auto l = right.branches[right.branches.size() - 2]; 1149 | auto r = right.branches[right.branches.size() - 1]; 1150 | // computation goes to the left argument, the right one will be then popped 1151 | right.branches[right.branches.size() - 2] = binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 1152 | auto ls = rank_1_to_string(lt); 1153 | auto rs = rank_1_to_string(rt); 1154 | return strings_to_rank_2(Strings::split(ls, rs)); 1155 | }); 1156 | 1157 | right.branches.pop_back(); 1158 | execute(left, right); 1159 | } else if(right.branches.back().leaf == 'D') { // join strings 1160 | right.branches.pop_back(); 1161 | if(right.branches.size() < 2) { 1162 | cerr << "Syntax error: binary operation D (AKA join) requires 2 arguments\n"; 1163 | return; 1164 | } 1165 | auto l = right.branches[right.branches.size() - 2]; 1166 | auto r = right.branches[right.branches.size() - 1]; 1167 | // computation goes to the left argument, the right one will be then popped 1168 | right.branches[right.branches.size() - 2] = binary_2_to_1(l, r, [](const The& lt, const The& rt) -> The{ 1169 | auto lss = rank_2_to_strings(lt); 1170 | auto rs = rank_1_to_string(rt); 1171 | return string_to_rank_1(Strings::join(lss, rs)); 1172 | }); 1173 | right.branches.pop_back(); 1174 | execute(left, right); 1175 | } else if(right.branches.back().leaf == 'V') { // filter by a logical value 1176 | right.branches.pop_back(); 1177 | if(right.branches.size() < 2) { 1178 | cerr << "Syntax error: binary operation V (AKA filter) requires 2 arguments\n"; 1179 | return; 1180 | } 1181 | auto l = right.branches[right.branches.size() - 2]; 1182 | auto r = right.branches[right.branches.size() - 1]; 1183 | right.branches[right.branches.size() - 2] = filtered(l, r); 1184 | right.branches.pop_back(); 1185 | execute(left, right); 1186 | } else if(right.branches.back().leaf == 'b') { // load from file 1187 | right.branches.pop_back(); 1188 | if(right.branches.size() < 1) { 1189 | cerr << "Syntax error: b operation (AKA file loading) has no path to load from\n"; 1190 | return; 1191 | } 1192 | right.branches.back() = unary_1(right.branches.back(), [](const The& the) -> The{ 1193 | auto path = rank_1_to_string(the); 1194 | if(filesystem::is_directory(path)) { 1195 | vector list; 1196 | for (const auto & entry : filesystem::directory_iterator(path)) 1197 | list.push_back(entry.path()); 1198 | return strings_to_rank_2(list); 1199 | } else { 1200 | ifstream t(path); 1201 | string content((std::istreambuf_iterator(t)), 1202 | std::istreambuf_iterator()); 1203 | return string_to_rank_1(content); 1204 | } 1205 | }); 1206 | execute(left, right); 1207 | } else if(right.branches.back().leaf == 'p') { // save to file 1208 | right.branches.pop_back(); 1209 | if(right.branches.size() < 1) { 1210 | cerr << "Syntax error: p operation (AKA file storing) requires 2 arguments\n"; 1211 | return; 1212 | } 1213 | auto l = right.branches[right.branches.size() - 2]; 1214 | auto r = right.branches[right.branches.size() - 1]; 1215 | // write to file, discard the returned The 1216 | binary_1_to_1(l, r, [](const The& lt, const The& rt) -> The{ 1217 | auto what = rank_1_to_string(lt); 1218 | auto where = rank_1_to_string(rt); 1219 | std::ofstream out(where); 1220 | out << what; 1221 | out.close(); 1222 | return The(); 1223 | }); 1224 | right.branches.pop_back(); 1225 | execute(left, right); 1226 | } else if(right.branches.back().leaf == 'o') { // delete file 1227 | right.branches.pop_back(); 1228 | if(right.branches.size() < 1) { 1229 | cerr << "Syntax error: o operation (AKA delete file) has no path to delete\n"; 1230 | return; 1231 | } 1232 | unary_1(right.branches.back(), [](const The& the) -> The{ 1233 | auto path = rank_1_to_string(the); 1234 | if(filesystem::is_directory(path)) { 1235 | filesystem::remove_all(path); 1236 | } else { 1237 | filesystem::remove(path); 1238 | } 1239 | return The(); 1240 | }); 1241 | right.branches.pop_back(); 1242 | execute(left, right); 1243 | } else if(right.branches.back().leaf == 'e') { // help 1244 | cout << "This is the nameless programming language.\n\n"; 1245 | cout << "Running the tests...\n"; 1246 | tests(); 1247 | cout << "All tests pass.\n"; 1248 | cout << "Rename this executable into a valid program and run it again."; 1249 | right.branches.clear(); 1250 | } else { 1251 | cerr << "The command doesn't know what " << right.branches.back().leaf << " is supposed to mean\n"; 1252 | return; 1253 | } 1254 | } else { 1255 | right.branches.push_back(left.branches.back()); 1256 | left.branches.pop_back(); 1257 | execute(left, right); 1258 | } 1259 | } 1260 | 1261 | string process_to_string(string in){ 1262 | The left; 1263 | left.is_leaf = false; 1264 | const auto in_size = in.size(); 1265 | left.branches.resize(in_size); 1266 | // store chars in the reverse order 1267 | for(auto i = 0u; i < in_size; ++i) 1268 | left.branches[i].leaf = in[in_size - i - 1]; 1269 | 1270 | The right; 1271 | right.is_leaf = false; 1272 | execute(left, right); 1273 | return to_string(right); 1274 | } 1275 | 1276 | bool equal_or_print(string a, string b, string c) { 1277 | if(a.empty() && b.empty()) 1278 | return true; 1279 | if(a.empty() && !b.empty()) { 1280 | cout << "left < right; rigth: " << b << "\n"; 1281 | return false; 1282 | } 1283 | if(!a.empty() && b.empty()) { 1284 | cout << "left > right; left: " << a << "\n"; 1285 | return false; 1286 | } 1287 | if(a[0] != b[0]) { 1288 | cout << "\"" << c+a[0] << "\" != \"" << c+b[0] << "\"\n"; 1289 | cout << int(a[0]) << " != " << int(b[0]) << "\n"; 1290 | return false; 1291 | } 1292 | c += a[0]; 1293 | return equal_or_print(a.substr(1), b.substr(1), c); 1294 | } 1295 | 1296 | 1297 | void tests() { 1298 | // comments, symbols 1299 | assert(process_to_string("test1._comment") == "test1\n"); 1300 | assert(process_to_string("substitutes:U_Z_N_i_J_L_I_Y_") == "substitutes:_/\\.\n \'\"\n"); 1301 | 1302 | // elevation, deelevation, replication 1303 | assert(process_to_string("test^_2^_") == "\ttest\n\t2\n\n"); 1304 | assert(process_to_string("test^_2^_^_v_") == "\ttest\n\t2\n\n"); 1305 | assert(process_to_string("test^_3^_m_") == "\t\ttest\n\t\ttest\n\t\ttest\n\n\n"); 1306 | 1307 | // indexes 1308 | assert(process_to_string("1^_2^_3^_2^_|_") == "\t1\n\t2\n\t3\n\t1\n\n"); 1309 | assert(process_to_string("1^_2^_3^_2^_0^_#_") == "3\n"); 1310 | assert(process_to_string("1^_2^_3^_^_2^_1^_#_") == "\t3\n\n"); 1311 | assert(process_to_string("1^_2^_3^_^_4^_5^_6^_^_^_2^_2^_#_") == "\t\t3\n\t\t6\n\n\n"); 1312 | 1313 | // + - * / 1314 | assert(process_to_string("2^_3^_+_") == "\t5\n\n"); 1315 | assert(process_to_string("4^_3^_-_") == "\t1\n\n"); 1316 | assert(process_to_string("4^_3^_x_") == "\t12\n\n"); 1317 | assert(process_to_string("12^_3^_z_") == "\t4\n\n"); 1318 | assert(process_to_string("1^_2^_^_3^_+_") == "\t\t4\n\t\t5\n\n\n"); 1319 | assert(process_to_string("1^_2^_^_3^_4^_^_+_") == "\t\t4\n\t\t6\n\n\n"); 1320 | 1321 | // strings 1322 | assert(process_to_string("pre,the,post^_,^_E_") == "\t\tpre\n\t\tthe\n\t\tpost\n\n\n"); 1323 | assert(process_to_string("pre,the,post^_,^_E_H_&_") == "\t\tprepre\n\t\tthethe\n\t\tpostpost\n\n\n"); 1324 | assert(process_to_string("pre,the,post^_,^_E_-^_D_") == "\tpre-the-post\n\n"); 1325 | assert(process_to_string("pre,the,post^_,^_E_H_p^_)_V_") == "\t\tpre\n\t\tpost\n\n\n"); 1326 | assert(process_to_string("pre,the,post^_,^_E_A_D_") == "\tprethepost\n\n"); 1327 | assert(process_to_string("pre,,post^_,^_E_A_D_") == "\tprepost\n\n"); 1328 | assert(process_to_string("pre1,123,4post^_,^_E_C_v_") == "\t0\n\t123\n\t0\n\n"); 1329 | 1330 | // files 1331 | assert(process_to_string("build.sh^_b_J_^_E_H_rm^_)_V_$_") == "\t1\n\n"); 1332 | assert(process_to_string(".^_b_H_build.sh^_)_V_$_") == "\t1\n\n"); 1333 | assert(process_to_string("test^_test.txt^_p_test.txt^_b_=_test.txt^_o_") == "\t1\n\n"); 1334 | 1335 | // stack 1336 | assert(process_to_string("1^_2^_3^_^_$_") == "\t3\n\n"); 1337 | assert(process_to_string("1^_2^_3^_H_") == "\t1\n\t2\n\t3\n\t3\n\n"); 1338 | assert(process_to_string("1^_2^_3^_X_") == "\t1\n\t2\n\n"); 1339 | assert(process_to_string("1^_2^_3^_G_") == "\t1\n\t3\n\t2\n\n"); 1340 | 1341 | // logic 1342 | assert(process_to_string("1,2^_1,2^_=_") == "\t1\n\n"); 1343 | assert(process_to_string("1,2^_2,2^_=_") == "\t0\n\n"); 1344 | assert(process_to_string("1,2^_1,20^_%_") == "\t1\n\n"); 1345 | assert(process_to_string("1,2^_2,20^_%_") == "\t0\n\n"); 1346 | assert(process_to_string("alice^_alice^_=_") == "\t1\n\n"); 1347 | assert(process_to_string("alice^_bob^_=_") == "\t0\n\n"); 1348 | assert(process_to_string("alice^_alice^_=_T_") == "\t0\n\n"); 1349 | assert(process_to_string("alice^_bob^_=_T_") == "\t1\n\n"); 1350 | assert(process_to_string("1,2^_2,2^_<_") == "\t1\n\n"); 1351 | assert(process_to_string("3,2^_2,2^_<_") == "\t0\n\n"); 1352 | assert(process_to_string("1^_1^_W_") == "\t1\n\n"); 1353 | assert(process_to_string("1^_0^_W_") == "\t0\n\n"); 1354 | assert(process_to_string("0^_1^_W_") == "\t0\n\n"); 1355 | assert(process_to_string("0^_0^_W_") == "\t0\n\n"); 1356 | assert(process_to_string("1^_1^_M_") == "\t1\n\n"); 1357 | assert(process_to_string("1^_0^_M_") == "\t1\n\n"); 1358 | assert(process_to_string("0^_1^_M_") == "\t1\n\n"); 1359 | assert(process_to_string("0^_0^_M_") == "\t0\n\n"); 1360 | 1361 | // substrings 1362 | assert(process_to_string("b^_bob^_(_") == "\t1\n\n"); 1363 | assert(process_to_string("bob^_bob^_(_") == "\t1\n\n"); 1364 | assert(process_to_string("alice^_bob^_(_") == "\t0\n\n"); 1365 | assert(process_to_string("bob^_b^_)_") == "\t1\n\n"); 1366 | assert(process_to_string("bob^_bob^_)_") == "\t1\n\n"); 1367 | assert(process_to_string("alice^_bob^_)_") == "\t0\n\n"); 1368 | assert(process_to_string("bob^_bob^_[_") == "\t1\n\n"); 1369 | assert(process_to_string("alice^_bob^_[_") == "\t0\n\n"); 1370 | assert(process_to_string("bobcat^_bob^_[_") == "\t1\n\n"); 1371 | assert(process_to_string("bob^_bob^_]_") == "\t1\n\n"); 1372 | assert(process_to_string("alice^_bob^_]_") == "\t0\n\n"); 1373 | assert(process_to_string("catbob^_bob^_]_") == "\t1\n\n"); 1374 | } 1375 | 1376 | int main(int argc, char *argv[]){ 1377 | auto program = filesystem::path(argv[0]).filename(); 1378 | cout << process_to_string(program); 1379 | cout << endl; 1380 | return 0; 1381 | } 1382 | --------------------------------------------------------------------------------