Started at 2022-06-09T20:51:55+00:00; stage 2 since 2022-06-25T20:51:55+00:00. Ended at 2022-07-09T20:51:55+00:00.
The task for the first round will be sorting lists of integers.
The test cases are as follows:
[] => []
[172] => [172]
[203, 35] => [35, 203]
[18, 13, 94] => [13, 18, 94]
[193, 46, 26, 129] => [26, 46, 129, 193]
[77, 108, 182, 111, 242] => [77, 108, 111, 182, 242]
[88, 159, 106, 233, 87, 30] => [30, 87, 88, 106, 159, 233]
[151, 106, 165, 236, 180, 249, 113] => [106, 113, 151, 165, 180, 236, 249]
[117, 238, 17, 93, 165, 76, 166, 15] => [15, 17, 76, 93, 117, 165, 166, 238]
[115, 7, 181, 141, 13, 205, 86, 231, 135] => [7, 13, 86, 115, 135, 141, 181, 205, 231]
[118, 23, 77, 47, 177, 48, 15, 111, 25, 75] => [15, 23, 25, 47, 48, 75, 77, 111, 118, 177]
[106, 220, 114, 44, 57, 117, 169, 56, 198, 109, 165] => [44, 56, 57, 106, 109, 114, 117, 165, 169, 198, 220]
[30, 15, 164, 86, 18, 200, 93, 145, 224, 249, 134, 160] => [15, 18, 30, 86, 93, 134, 145, 160, 164, 200, 224, 249]
[167, 230, 10, 83, 226, 93, 97, 218, 98, 51, 115, 236, 94] => [10, 51, 83, 93, 94, 97, 98, 115, 167, 218, 226, 230, 236]
[248, 106, 26, 120, 32, 72, 218, 78, 178, 37, 76, 15, 137, 218] => [15, 26, 32, 37, 72, 76, 78, 106, 120, 137, 178, 218, 218, 248]
[214, 158, 209, 243, 75, 148, 216, 195, 223, 137, 169, 90, 142, 109, 1] => [1, 75, 90, 109, 137, 142, 148, 158, 169, 195, 209, 214, 216, 223, 243]
1. olus2000: +7 -1 = +6
2. SoundOfSpouting: +6 -1 = +5
3. IFcoltransG: +3 -3 = 0
4. firecubez: +0 -0 = 0
5. Palaiologos: +0 -1 = -1
6. umnikos: +1 -3 = -2
7. Ardemit: +0 -2 = -2
8. MattiDragon: +0 -3 = -3
8. RocketRace: +0 -3 = -3
Solver => Solved
===================================
olus2000 => Palaiologos
olus2000 => umnikos
olus2000 => Ardemit
olus2000 => SoundOfSpouting
olus2000 => RocketRace
olus2000 => IFcoltransG
olus2000 => MattiDragon
SoundOfSpouting => olus2000
SoundOfSpouting => MattiDragon
SoundOfSpouting => Ardemit
SoundOfSpouting => umnikos
SoundOfSpouting => RocketRace
SoundOfSpouting => IFcoltransG
IFcoltransG => umnikos
IFcoltransG => RocketRace
IFcoltransG => MattiDragon
umnikos => IFcoltransG
Written by IFcoltransG
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | <!--Please note that this specification is written in PLAIN TEXT as per the Esolang Buffet rules--> # Rabbitsfoot > “I think that someone will remember us in another time.” > — Sappho of Lesbos (Lobel & Page, Fragment 147) The Rabbitsfoot language employs the revolutionary new "rabbitsfoot" paradigm, which some liken to the sweet-spot between blockchain, quantum computing, and big data. It also employs the imperative paradigm, which is counter-revolutionary and old. It was designed by IFcoltransG in 2022 for the first inaugural Esolangs Buffet event on the Unofficial Esolangs Discord Server. (The first inaugural one, not for any other inaugural ones after that.) Rabbitsfoot's file extension is `.rtf` for programs, or `.rtfm` for Rabbitsfoot library modules. ## Usage Run the interpreter on your program from the command line. ```sh # regular mode python ./rabbitsfoot.py FILENAME # verbose mode: python ./rabbitsfoot.py -v FILENAME python ./rabbitsfoot.py --verbose FILENAME ``` The interpreter will read from standard input a list of integers. The integers are separated by whitespace and/or commas, and can have optional `[` and `]` on each end. The list is terminated by a newline. Output is written like a Python list. ### Example inputs `1 2 3 4 5` `[1, 2, 3, 4, 5]` ### Example program ```rabbitsfoot # cat , . ``` ### Example output for example inputs into example program `[1, 2, 3, 4, 5]` ## Execution The interpreter stores a tuple of W indices called the index tuple, where W is the window size. The interpreter stores an array of size N called the array, where N is the number of integers in the program input. The interpreter stores a stack, called the stack. The interpreter optionally stores a single value, called the cache. Initially, the array is set to the program input. Initially, the index tuple is set to (0, ..., 0). Initially, the stack is empty. If N is ever 0, the program terminates immediately and outputs its results. ### Input Whenever input is requested, a list V of size W is pushed onto the stack. The i-th element of V is taken from the array at index j, where j is the i-th element of the index tuple. For instance, if W=N and the index tuple is (0, 1, ..., N-1), then V has the exact same elements as the array. If W=2, N>0 and the index tuple is (0, 0), then V will be [X, X], where X is the first element of the array. After the array is initialised, the program input itself is treated as compromised and must no longer be trusted. Subsequent requests for input will treat the array as the only source of truth. ### Basic Loop The user's program is run, line by line, from the first line, until any control flow statements are reached. ### Stack The stack can contain integers, lists of integers or strings. However, the stack cannot contain its excitement whenever it sees a bunny rabbit. If a value is popped from an empty stack, that is undefined behaviour. So don't pop from an empty stack. ### Window size W is equal to 1 if the program contains no `[`...`]` instructions. Otherwise, it is inferred so that it fits with a `[`...`]` instruction. ## Instructions All lines must exactly comport to one or more of the following standards. Except where otherwise noted, commands must take up between 0 and 2 lines (exclusive) with only whitespace left over. Punctuation that corresponds with endorsed operations have a newline automatically inserted after them, unless they are part of a comment. ### Comments Any line containing only whitespace is ignored. Any line beginning with `#` or `REM ` is ignored. It's nothing personal, and the interpreter ignoring your code doesn't reflect on your value as a person. ### Loop The `.` command pops a list from top-of-stack, then iterates through each index i in it, storing the i-th value of the list into the array index given by the i-th element of the index tuple. If the value at top-of-stack is not a list, that is undefined behaviour. (If it helps, imagine the list is a list of rabbits. Then the `.` command pops the list off the stack, and lets all of the rabbits loose, so that they find the value in the index tuple that was at the same position as them, and gnaw at the index in the array given by the value they found, until that array location becomes the value corresponding to that rabbit.) Then the interpreter increments that the index tuple and then jumps to the start of the program. (Implementations must preserve the stack.) All programs must contain at least one `.`. Programs that contain only comments are undefined behaviour. To increment the index tuple, add one to its rightmost value. Then until no elements of the tuple equal N, pick any element that equals N, set it to exactly 0, then add one to the element to its left in the tuple. If no such element exists, the program terminates immediately and outputs its results. ### Literal The `[`...`]` command pushes a given list of integers onto the stack. Between the `[` and the `]` are a sequence with length W of whitespace separated integers. Patent pending. ### Zilch The `0` command pushes a 0 to the stack. If both N and W equal 0, or if the index tuple is (0, ..., 0), then it is undefined behaviour to run `0`. That would be too many zeros. It's safer this way. ### Memory The first time `|` is executed, it peeks at the top value of the stack and saves it to the cache. Any subsequent time it is run, it pops the top-of-stack and pushes a copy of the value saved in the cache. It is undefined behaviour for `|` to be executed when the stack is empty. ### Addition The `+` command pops two integers or two lists of integers from the stack, adds them (elementwise in the case of two lists), then pushes the result. Implementations are free to add the values in any order they choose. However, they should be forewarned that addition is commutative, meaning the implementator's choice is just as meaningless as every other choice they make in their lives. ### Reverse Factorisation The `*` pops integers or two lists of integers from the stack, and pushes their product or elementwise product, respectively. Again, implementations may left-multiply or right-multiply. It's their prerogative. Commutativity of multiplication is left as an exercise to the reader. ### Bounce When the `!` command is run, if the value at top-of-stack is an integer, the interpreter jumps to the next `!`. Otherwise, if the value at top-of-stack is a list or a string, the command is treated as a no-op. It is undefined behaviour for a program to have an odd number of `!`. The value at top-of-stack is not popped. It is undefined behaviour to run `!` if the stack is empty, or for the interpreter to try to jump to the next `!` when there are not more left. ### Compare The `-` command pops two integers from the stack and pushes the max of the two integes, then pushes the negative of the min of the two integers. If the first value popped is a list rather than an integer, no second value is popped. Instead, every integer in the list is replaced with 1, 0 or -1 corresponding to if it is positive, zero, or negative, respectively, then the resulting list is pushed again. Maximum picks closest to ∞ and minimum picks closest to 0. ### Factorial The `?` command performs a factorial operation on the integer at top-of-stack. If you are wondering why the symbol isn't `!`, it's because that symbol was taken. It is undefined behaviour for `?` to be run on a string or list, or for the integer to be negative. ### Pack The `$` command checks if there are exactly W integers on the stack. If so, it pops those integers and pushes a list containing them. If not, that is undefined behaviour. You can imagine it like having a stack filled with rabbits at nighttime, then when day breaks the rabbits go back to the warren (as long as there are exactly the right amount of rabbits) and the warren is pushed to the stack. It's like that. ### Division The `/` command elementwise divides the list at top-of-stack by W, rounding down. If the value at top-of-stack is a string or an integer rather than a list of integers, that is undefined behaviour. ### Input Request To request input data to process, the programmer needs only leave a line with a lone `,` in it, which strikes me as awfully convenient. Don't you think? ### Eval The `^` command pops a string from the stack, splits it by newlines, then runs each line of code sequentially. It is illegal for the string to contain a `.` instruction (though it may contain further `^` instructions). Trying to execute an integer or a list is undefined behaviour. Imagine that your string is actually a rabbit. Then running `^` causes that rabbit to be popped from the stack and executed. Poor rabbit. ### Exec The `@` command executes the commands from the first line of the source code. Note that many instructions may be executed in this way if they didn't have newlines following them pre-endorsement. It is undefined behaviour for `@` to be placed on the first line of the source code. ### Transmogrification The `~` command pops W lists from the stack and assembles a 2-d grid of integers, where if an integer is at position x of the y-th popped list, it is put at position (x, y-1). Then for each x coordinate χ starting from 0, the interpreter pushes a list of every integer on a grid point that satisfies x=χ, starting from y=0. It may help understanding if you imagine that there are rabbits arranged in a little grid. (The rabbits are the contents of the popped lists.) Then the rabbits do a dance, and end up in the positions described above. Then the lines of rabbits are pushed onto the stack. If any value pushed is not a list of integers, that is undefined behaviour. ### Random The `%` command pops an integer or a list. If an integer is popped, a uniform random integer between 0 and that integer (inclusive) is pushed. If a list is popped, it is pushed shuffled. Should a negative integer be popped this way, the behaviour is undefined. ### Increment The `<`...`>` command increments a series of indices. Between the `<` and `>` are a sequence of whitespace-separated non-negative integers. For each number from left to right, the corresponding array element at that index is increased by 1. ### Semantic comments The `=` command is a little like a macro. It inserts the text of the first comment in the source code into the program at the position of the `=`, uncommented. It is undefined behaviour for `=` to come before the first comment in the program. Fun animal fact: the `=` sign looks like two rabbit ears! ## Endorsements and Awards The following instructions are certified and endorsed by the Rabbitsfoot Lucky-Paw Seal of Approval (RLPoA): ### Arithmetic operators These symbols deserve respect for their longstanding contribution to the buttons on calculators. - `+` - `*` - `/` ### Other brainfuck symbols These symbols have contributed meaningfully to the history of esolangs. - `,` - `.` - `[`...`]` - `<`...`>` ### Who knows really These symbols are financial benefactors of the Rabbitsfoot programming language. - `~` - `|` - `!` - `?` The symbol `-` has received an *honorary* endorsement in appreciation of its substantial contributions to `[`...`]` and to arithmetic. Due to a technicality, `-` alas cannot receive implicit newline privileges, but was awarded a small cash koha instead. ## Rabbitsfoot library modules Files with the `.rtfm` extension are treated as Rabbitsfoot library modules rather than programs. Rabbitsfoot library modules are executed as if they were programs. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | import pathlib import itertools import sys def cmp(a, b): # taken from https://docs.python.org/3.0/whatsnew/3.0.html#ordering-comparisons return (a > b) - (a < b) def flip(x): # transposes return [list(row) for row in zip(*x, strict=True)] def factorial(n): for i in range(1, n): n *= i return max(n, 1) def is_comment(line): return line.startswith("#") or line.startswith("REM ") # read command line arguments if len(sys.argv) < 2: print("Please pass filename of code", file=sys.stderr) sys.exit(1) verbose_mode = "-v" in sys.argv or "--verbose" in sys.argv filename = sys.argv[-1] code = pathlib.Path(filename).read_text() if verbose_mode: # enable logging to stderr log = lambda *a, **k: print(*a, **k, file=sys.stderr) else: # disable logging log = lambda *a, **k: None # read list of integers from stdin data = [ int(num) for num in input() .replace(",", " ") .strip("[ ]").split() ] if not code.split("\n"): raise SyntaxError("Code has no lines") # preprocessing # replace all @ macros with the first line of the program first_line = code.split("\n")[0] if "@" in first_line: raise SyntaxError("`@` on first line of program") code = code.replace("@", first_line) # find first comment for the = command for line in code.split("\n"): if is_comment(line): first_comment = line.lstrip("REM ").lstrip("#") break if "=" in line: raise SyntaxError("`=` before first comment") else: first_comment = "" # remove comments code = "\n".join( line for line in code.split("\n") if not is_comment(line) ) # insert macros from first comment if "=" in code: code = code.replace("=", first_comment) # insert newlines after endorsed commands endorsed = {"+", "*", "/", "~", "|", ",", ".","]", ">", "!", "?"} for endorsement in endorsed: # insert newlines automatically code = code.replace(endorsement, endorsement + "\n") # get window size window_size = 1 for line in code.split("\n"): if line.startswith("[") and line.endswith("]"): # found a [...] line # so can infer size of window window_size = len(line.strip("[ ]").split()) break # otherwise default to 1 log(f"{window_size = }") stack = [] cache = None ranges = [range(len(data)) for _ in range(window_size)] for indices in itertools.product(*ranges): log(f"Iterating with {indices = }") for line in code.split("\n"): line = line.strip() log(f" Runnning line: {line}") match line: case ",": # get input from array stack.append([data[i] for i in indices]) case "*": # multiply top = stack.pop() second = stack.pop() match (top, second): case (int(), int()): stack.append(top * second) case (list(), list()): product = [x*y for x,y in zip(top, second, strict=True)] stack.append(product) case _: raise TypeError("Mixed-type multiplication is not supported") case "+": # add top = stack.pop() second = stack.pop() match (top, second): case (int(), int()): stack.append(top + second) case (list(), list()): summation = [x+y for x,y in zip(top, second, strict=True)] stack.append(summation) case _: raise TypeError("Mixed-type addition is not supported") case "?": # factorial top = stack.pop() if isinstance(top, int): if top < 0: raise ValueError("Attempted factorial on negative number") product = factorial(top) stack.append(product) else: raise TypeError("Factorial on something other than an integer") case "-": # get max/min of two ints or elementwise get sign top = stack.pop() if isinstance(top, int): second = stack.pop() stack.append(max(top, second)) stack.append(-min(top, second), key=abs) else: sign = [cmp(x, 0) for x in top] stack.append(sign) case "|": # save/load memory if cache is None: cache = stack[-1] log(f" -cache set to {cache}") else: stack.pop() stack.append(cache) case "~": # flip about main diagonal rows = [stack.pop() for col in range(window_size)] cols = flip(rows) cols.reverse() stack.extend(cols) case "0": # push 0 if indices == (0,) * window_size: raise ValueError("Tried to execute `0` when index tuple was all zeros") elif len(data) == 0 and window_size == 0: raise ValueError("Tried to execute `0` when window size and input size were both 0. Very dangerous!") else: stack.append(0) case "$": # pack integers into list and push integers = [] while stack: next_integer = stack.pop() if not isinstance(next_integer, int): raise TypeError("Tried to pack non-integer into list of integers") integers.append(next_integer) if len(integers) != window_size: raise ValueError("Wrong number of popped integers") stack.append(integers) case "!": # conditional jump basic_count = code.replace(" ", "").replace("\t", "").count("\n!\n") if code.startswith("!") != code.endswith("!"): basic_count += 1 if basic_count % 2 == 1: raise SyntaxError("Odd number of `!`") if isinstance(stack[-1], int): # this code path shouldn't happen raise NotImplementedError() case "^": # eval eval_code = stack.pop() if isinstance(code, str): for eval_line in eval_code.split("\n"): if eval_line == "": continue if eval_line.strip() == ".": raise ValueError("Tried to execute a `.` within a `^` evaluation") # this code path shouldn't happen raise NotImplementedError() else: raise TypeError("Tried to execute something other than a string") case "/": # divide top = stack.pop() quotient = [num // window_size for num in top] stack.append(quotient) case "%": # randomise top = stack.pop() match top: case int(): if top < 0: raise ValueError("Random integer from empty set") stack.append(random.randrange(0, top + 1)) case list(): top = list(top) random.shuffle(top) stack.append(top) case x if x.startswith("[") and x.endswith("]"): # push literal values = [ int(num) for num in x.strip("[ ]").split() ] if len(values) != window_size: raise SyntaxError("Literal does not match window size") stack.append(values) case x if x.startswith("<") and x.endswith(">"): # increment indices of array if "-" in x: raise ValueError("Negative integer in indices to increment") values = [int(num) for num in x.strip("< >").split()] for index in values: data[index] += 1 case ".": # end program break #case ":": # # deprecated duplicate # top = stack.pop() # stack.append(top) # stack.append(top) #case ";": # # deprecated swap # top = stack.pop() # second = stack.pop() # stack.append(top) # stack.append(second) case "": # empty line continue case _: raise SyntaxError(f"Got malformed line: {line}") log(f" {stack = } <-TOS") else: # program didn't end with "." raise SyntaxError("Program does not contain a `.` instruction") # this iteration, we replace all chosen values # with new values calculated by the program new_values = stack.pop() log(f"Replacing values at indices {indices} with {new_values}") for index, value in zip(indices, new_values, strict=True): data[index] = value log(f"{cache = }") log(f"{data = }") log() print(data) log() log("---") log("Program terminated successfully.") |
Written by olus2000
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | from sys import argv ''' list -> quotation int -> number str -> operator ''' # Will break on 999-deep nested quotations, but I think that's reasonable. def parse_program(p: str, i: int = 0) -> (list, int): program = [] while i < len(p) and p[i] != ']': if p[i] in '~!+': program.append(p[i]) elif p[i] == '[': start = i quote, i = parse_program(p, i + 1) if i >= len(p) or p[i] != ']': raise Exception(f'Unclosed quotation at {start}') program.append(quote) i += 1 return program, i def parse_input(): return [int(s) for s in input().split()] # When popping two items 'a' will be the leftmost value, 'b' the rightmost. def reduce(p, dat): ret = [p] ip = [0] while ret: if ip[-1] >= len(ret[-1]): ret.pop() ip.pop() else: ## print(ret, ip, dat, sep='\n') ## print() com = ret[-1][ip[-1]] ip[-1] += 1 match com: case int() as i: dat.append(i) case list() as q: dat.append(q) case '~': if len(dat) > 1: match dat.pop(), dat.pop(): case int(a), int(b): if a < b: dat.append(a) dat.append(b) else: dat.append(b) dat.append(a) case a, b: dat.append(a) dat.append(b) case '!': if len(dat) < 2 or not isinstance(dat[-1], list): dat = [] else: a, b = dat.pop(), dat.pop() ret.append(a) ip.append(0) case '+': if len(dat) < 2 or not isinstance(dat[-1], list): dat = [] else: a, b = dat.pop(), dat.pop() dat.append([b] + a) dat.append(a + [b]) case _: raise Exception(f'Unexpected item in the program: {com}') ans = [] for i in reversed(dat): if isinstance(i, int): ans.append(i) else: break ## print(ans) ## print(dat) return list(reversed(ans)) if __name__ == '__main__': if len(argv) != 2: raise Exception(f'Wrong number of arguments: {len(argv) - 1}') with open(argv[1], 'r') as f: program, _ = parse_program(f.read()) out = reduce(program, parse_input()) for i in out: print(i, end=' ') print() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | # Stack sorter Stack sorter is a concatenative language based on the concatenative calculus. It operates via the means of expression rewriting. Expressions may consist of: - operators - quotations - numbers (only during runtime, not representable in code) ### Operation The program is parsed into an expression according to the rules explained below. Input is parsed as a string of whitespace-separated decimal numbers ending in a newline and prepended to the expression. Any parsing errors may produce undefined behavior. The expression is scanned for patterns related to words. The right-most match is replaced with another expression according to the rule for that pattern. This match-replace cycle is repeated until there are no matches. Trailing numbers in the resulting expression are the output of the program. Only numbers that are outside quotations count for output. ### Operators and patterns IMPORTANT: patterns don't match within quotations. There are several words that can be used in the stack sorter, every one coming with a pattern and a substitution rule. In the examples letters `X` and `Y` represent arbitrary quotations or numbers, letters `N` and `M` represents numbers and letter `A` represents an arbitrary expression. Spaces represent any non-zero number of whitespace delimiters. - `NM~` -> `NM` if N <= M, else `MN` - `XY~` -> `YX` if X or Y is not a number, else the rule above matches. - `X~` -> `X` if previous two rules didn't match. - `X[A]+` -> `[XA][AX]` - `X[A]!` -> `A` Any characters except `[]~!+` in the program are discarded. ### Quotations In code quotations are represented as expressions in brackets: `[]`. Quotations can be manipulated using operators. Patterns don't match inside quotations, and numbers contained within them don't count as trailing numbers. If brackets in the program don't match it's a parsing error which produces undefined behavior. ### Examples Program `[]![]+~!` with input `1 2` will output `1`. I'm too lazy to write more. ### Interpreter Interpreter expects one parameter: a file with the program. It will parse the program, parse one line of input and print out the output if no errors occur. |
Written by MattiDragon
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | # Three Stack Bit Math This esolang has three stacks; A, B and X. The first two, A and B, are used for data storage. A will automatically contain any command line arguments after the program name. X is used for calculations. The main logic will be performed using bit magic as comparison operators don't exist. Blocks can be used for limited conditional logic. I haven't confirmed, but it seems to me like this esolang isn't turing complete. ## Blocks Blocks are the main conditional element in the language. The work by infinetly looping until a stack access is attempted on an empty stack. ## Instructions ### Block * `{`: Opens a block * `}`: Closes a block ### Constants * `0`: Loads the constant 0 to the X * `1`: Loads the constant 1 to the X ### Move * `a`: Moves one value from A to X * `b`: Moves one value from B to X * `c`: Moves one value from X to A * `d`: Moves one value from X to B * `e`: Copies all values from B to A * `f`: Copies all values from A to B * `g`: Reverses A * `h`: Reverses B ### Misc * `!`: Pops one value from X * `%`: Swaps the top two values of X * `.`: Pops and prints the top value of X * `$`: Dupelicates the top value of X ### Math and Bit Magic * `+`: Adds together the top two values of X * `&`: Performs a bitwise AND operation on the top two values of X * `|`: Performs a bitwise OR operation on the top two values of X * `^`: Performs a bitwise XOR operation on the top two values of X * `-`: Performs a bitwise NOT operation on the top value of X * `<`: Performs a bitshift left on the top two values of X * `>`: Performs a bitshift right on the top two values of X ### Debug * `@`: Forces the program to exit * `=`: Dumps the stacks' contents ## Usage Run the interpretter using python 3.10 or newer with the program as the first argument and the data as the rest |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | import sys INSTRUCTIONS = list("abcdefgh01!%.+&|^-<>$=@") Program = list[str | type("Program")] Stacks = tuple[list[int], list[int], list[int]] class ExecutionError(RuntimeError): pass def main(): filename, *data = sys.argv[1:] try: with open(filename, "r") as file: program = compile(file.read()) run(program, [int(value) for value in data]) except ExecutionError as e: print("Error while executing:", e) sys.exit(2) def compile(program: str) -> Program: stack: list[Program] = [[]] for char in program: if char in INSTRUCTIONS: stack[-1].append(char) elif char == "{": stack.append([]) elif char == "}": stack[-2].append(stack.pop()) if len(stack) > 1: raise ExecutionError("Not all blocks closed!") return stack[0] def run(program: Program, data: list[int]): run_block((data, [], []), program, main=True) def run_block(stacks: Stacks, block: Program, main: bool = False): a, b, x = stacks index: int = 0 max_index = len(block) - 1 try: while True: #print("Executing:", block[index], stacks) match block[index]: case "a": x.append(a.pop()) case "b": x.append(b.pop()) case "c": a.append(x.pop()) case "d": b.append(x.pop()) case "e": # Copy content only, no changing references a[:] = b[:] case "f": # Copy content only, no changing references b[:] = a[:] case "g": a.reverse() case "h": b.reverse() case "0": x.append(0) case "1": x.append(1) case "!": x.pop() case "%": x += (x.pop(), x.pop()) case ".": print(x.pop()) case [*content]: run_block(stacks, content) case "+": x.append(x.pop() + x.pop()) case "&": x.append(x.pop() & x.pop()) case "|": x.append(x.pop() | x.pop()) case "^": x.append(x.pop() ^ x.pop()) case "-": # Bitwise not, not mathematical negation x.append(~x.pop()) case ">": # Because of the stack, the order is reversed. For these operations order matters second, first = x.pop(), x.pop() x.append(first >> second) case "<": second, first = x.pop(), x.pop() x.append(first << second) case "$": x.append(x[-1]) case "=": print("Stacks:", *stacks) case "@": raise ExecutionError("Debug exit") if index >= max_index: if main: return index = 0 else: index += 1 except IndexError as e: if main: raise ExecutionError("Index out of bounds in main body!") from e if __name__ == "__main__": main() |
1 | The integers are passed as arguments to the interpreter, placing them on the A stack. The integers are printed using the `.` operator in sorted order. |
Written by SoundOfSpouting
1 | gcc -o interpreter interpreter.c |
1 2 3 4 5 6 7 | #!/usr/bin/python3 # // Code by SoundOfSpouting#6980 (UID: 151149148639330304) # Supplementary program for interpreter, compiles ordered pairs (a, b)(c, d) or list [1, 2, 3, 4] into raw bytes # Usage: ./compiler.py [input file] [output file] import sys;import re;open(sys.argv[2],"wb").write(bytes(map(int,re.sub("[^0123456789\s]","",re.sub("[\[\]{}(),]", " ", open(sys.argv[1],"r").read())).split()))) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | // Code by SoundOfSpouting#6980 (UID: 151149148639330304) // Usage: interpreter.exe [input file] [program file] #include <stdlib.h> #include <stdio.h> #include <string.h> const int debug = 0; int randInt (int m, int M) { return 4; // stub } void doBetter () { puts ("Please Just Do Better"); exit (randInt (1, 69420)); } void printList (int* list, int length) { printf ("["); for (int i = 0; i < length - 1; i++) printf("%d, ", list[i]); if (length < 1) puts ("]"); else printf ("%d]\n", list[length - 1]); } void printDisks (int** disks, int* tops, int pegs) { puts ("{"); for (int i = 0; i < pegs; i++) { printf (" %d ", i); printList (disks[i], tops[i] + 1); } puts ("}"); } int listToDisks (const int* list, const int length, int*** _disks, int** _tops) { int pegs = 2; for (int i = 0; i < length - 1; i++) if (list[i] > list[i + 1]) pegs++; int** disks = (int**) malloc (pegs * sizeof (int*)); int* tops = (int*) malloc (pegs * sizeof (int)); for (int i = 0; i < pegs; i++) { tops[i] = -1; disks[i] = (int*) malloc (length * sizeof (int)); for (int j = 0; j < length; j++) disks[i][j] = -1; // Not necessary } int i = pegs - 2, j = 0; for (int k = length - 1; k > 0; k--) { disks[i][j] = list[k]; if (list[k-1] > list[k]) { tops[i] = j; i--; j = 0; } else j++; } disks[i][j] = list[0]; tops[i] = j; *_disks = disks; *_tops = tops; return pegs; } void disksToList (int* list, int** disks, const int* tops, const int pegs) { for (int j = 0; j < pegs; j++) for (int k = tops[j]; k >= 0; k--) (list++)[0] = disks[j][k]; } int move (int** disks, int* tops, const int pegs, const int length, int i, int j) { i = i % pegs; j = j % pegs; if (i == j || tops[i] < 0 || tops[j] > length - 2 || tops[j] > -1 && (disks[i][tops[i]] > disks[j][tops[j]])) return 0; tops[j]++; disks[j][tops[j]] = disks[i][tops[i]]; disks[i][tops[i]] = -1; // Not necessary tops[i]--; return 1; } int loadBytes (char* file, int** _list) { FILE* fp = fopen (file, "rb"); if (fp == NULL) doBetter (); fseek (fp, 0, SEEK_END); const int length = ftell (fp); fseek (fp, 0, SEEK_SET); int* list = (int*) malloc (length * sizeof (int)); for (int i = 0; i < length; i++) list[i] = fgetc (fp); fclose (fp); *_list = list; return length; } void execute (int* list, int length, int* program, int programLength) { if (debug > 5) printList (list, length); int** disks; int* tops; const int pegs = listToDisks (list, length, &disks, &tops); if (debug > 6) printDisks(disks, tops, pegs); for (int i = 0; i < programLength - 1; i += 2) { int s = move (disks, tops, pegs, length, program[i], program[i+1]); if (debug > 7) { printf ("(%d, %d)%s\n", program[i], program[i+1], s ? "" : " invalid"); if (s) printDisks (disks, tops, pegs); } } disksToList (list, disks, tops, pegs); for (int i = 0; i < pegs; i++) free (disks[i]); free (disks); free (tops); } int isSorted (int* list, int length) { int sorted = 1; for (int i = 0; i < length - 1; i++) if (list[i] > list[i+1]) { sorted = 0; break; } return sorted; } void sort (int* list, int length) { for (int i = 0; i < length; i++) for (int j = 0; j < length; j++) if (list[i] < list[j]) { int x = list[i]; list[i] = list[j]; list[j] = x; } } int* tests[] = { (int[]) {}, (int[]) {172}, (int[]) {203, 35}, (int[]) {18, 13, 94}, (int[]) {193, 46, 26, 129}, (int[]) {77, 108, 182, 111, 242}, (int[]) {88, 159, 106, 233, 87, 30}, (int[]) {151, 106, 165, 236, 180, 249, 113}, (int[]) {117, 238, 17, 93, 165, 76, 166, 15}, (int[]) {115, 7, 181, 141, 13, 205, 86, 231, 135}, (int[]) {118, 23, 77, 47, 177, 48, 15, 111, 25, 75}, (int[]) {106, 220, 114, 44, 57, 117, 169, 56, 198, 109, 165}, (int[]) {30, 15, 164, 86, 18, 200, 93, 145, 224, 249, 134, 160}, (int[]) {167, 230, 10, 83, 226, 93, 97, 218, 98, 51, 115, 236, 94}, (int[]) {248, 106, 26, 120, 32, 72, 218, 78, 178, 37, 76, 15, 137, 218}, (int[]) {214, 158, 209, 243, 75, 148, 216, 195, 223, 137, 169, 90, 142, 109, 1} }; void TRAILS (int* list, int length, int** subprograms, int* subprogramsLengthJ, int subprogramsLengthI) { const int requiredComplete = 3; int** cases[] = { (int*[]) { (int[]) {2,3,0,1}, (int[]) {1,3,0,2}, (int[]) {1,2,0,3}, (int[]) {0,3,1,2}, (int[]) {0,2,1,3} }, (int*[]) { (int[]) {0,0,1,0,0}, (int[]) {0,1,0,1,0}, (int[]) {0,1,1,1,0}, (int[]) {0,1,2,1,0}, (int[]) {0,2,1,2,0}, (int[]) {1,0,0,0,1}, (int[]) {1,0,1,0,1}, (int[]) {1,0,2,0,1}, (int[]) {1,1,0,1,1}, (int[]) {1,2,0,2,1}, (int[]) {2,0,1,0,2}, (int[]) {2,1,0,1,2} }, (int*[]) { (int[]) {} } }; int* casesLengthK[] = { (int[]) {4,4,4,4,4}, (int[]) {5,5,5,5,5,5,5,5,5,5,5,5}, (int[]) {0} }; int casesLengthJ[] = {5,12,1}; int casesLengthI = 3; int randCase = randInt (0, 14); cases[2][0] = tests[randCase]; casesLengthK[2][0] = randCase; int complete = 0; for (int i = 0; i < casesLengthI && i < subprogramsLengthI; i++) { if (debug > 4) printf ("TRAIL %d Begins\n", i); int flag = 1; for (int j = 0; j < casesLengthJ[i]; j++) { for (int k = i; k >= 0; k-=2) execute (cases[i][j], casesLengthK[i][j], subprograms[i], subprogramsLengthJ[i]); for (int k = i; k >= 0; k-=2) { if (debug > 5) { printf(" :: "); printList (cases[i][j], casesLengthK[i][j]); } if (!isSorted (cases[i][j], casesLengthK[i][j])) { if (debug > 4) printf ("You Have Failed TRAIL %d\n", i); flag = 0; break; } } if (flag == 0) break; if (i > 1) { execute (subprograms[i], subprogramsLengthJ[i], subprograms[i], subprogramsLengthJ[i]); if (debug > 5) { printf(" :: "); printList (subprograms[i], subprogramsLengthJ[i]); } if (!isSorted (subprograms[i], subprogramsLengthJ[i])) { if (debug > 4) printf ("You Have Failed TRAIL %d\n", i); flag = 0; break; } } if (flag == 0) break; } if (flag) { if (debug > 4) printf ("You Have Passed TRAIL %d\n", i); complete++; } } if (debug > 4) for (int i = subprogramsLengthI; i < casesLengthI; i++) printf ("You Have Failed TRAIL %d By Default\n", i); if (complete >= requiredComplete) { if (debug > 4) puts ("You Have Completed The TRAILS\nThe Input Will Now Be Sorted"); sort (list, length); } else if (debug > 4) printf ("You Have Not Completed Enough TRAILS\nPlease Complete %d More TRAILS", requiredComplete - complete); return; } int main (int argc, char** argv) { if (argc != 3) doBetter (); int* list; const int length = loadBytes (argv[1], &list); int* program; const int programLength = loadBytes (argv[2], &program); if (programLength > 100) doBetter (); if (program[0] == 255) { if (debug > 5) printList (list, length); if (debug > 4) puts ("TRAILS MODE ENGAGED"); int subprogramsLengthI = 0; for (int i = 0; i < programLength; i++) if (program[i] == 255) subprogramsLengthI++; int** subprograms = (int**) malloc (subprogramsLengthI * sizeof (int*)); int* subprogramsLengthJ = (int*) malloc (subprogramsLengthI * sizeof (int)); int j = programLength - 1, k = subprogramsLengthI - 1; for (int i = j; i >= 0; i--) if (program[i] == 255) { subprograms[k] = program + i + 1; subprogramsLengthJ[k] = (j - i); k--; j = i - 1; } TRAILS (list, length, subprograms, subprogramsLengthJ, subprogramsLengthI); free (subprograms); free (subprogramsLengthJ); } else execute (list, length, program, programLength); printList (list, length); int notSorted = 1 - isSorted (list, length); if (notSorted && debug > 5) { puts ("Not Sorted!"); if (debug > 8) { sort (list, length); printList (list, length); } } free (list); free (program); return notSorted; } |
1 2 3 | ./compiler.py ${1-inp.HNI} inp.BIN && ./compiler.py ${2-prg.HNI} prg.BIN && ./interpreter inp.BIN prg.BIN |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | The program inputs a list of integers from 0 to 255 (represented as bytes from the first file provided) The last line of output will be a visual representation of the same list but with elements in a different order (e.g. \x02\x00\x01\x03 -> [0, 1, 2, 3]) A program is a sequence of integers from 0 to 255 (represented as bytes from the second file provided) There may be at most 100 integers If the program starts with the value 255, see the TRAILS section Otherwise, the following occurs: The input list of integers is converted into an array of pegs, each of which holds a stack of disks (integers) In each peg, disks must be sorted in strictly-not-ascending order at all times The list is split into consecutive strictly-not-descending sub-lists, and sub-lists are placed into consecutive pegs from top to bottom There will be an empty peg after all of the sub-lists are placed For each pair of integers in the program (x, y) in order, the top disk from peg x is attempted to be moved to the top disk of peg y, where x and y are modulo the number of pegs Any extra integer not in a pair is ignored After the program is executed, the list is reconstructed - for each peg in order, all disks from top to bottom are added to the list Example program - sorts the list [193, 46, 26, 129] or \xc1\x2e\x1a\x81 (0, 3)(2, 0) \x00\x03\x02\x00 TRAILS There are many TRAILS (challenges) that a program can solve. It if solves at least all 3 of them, the input list is sorted automatically for you! Isn't that just dandy? The first 255 value is removed from the program, and the remaining values are split over the value 255 into several sub-programs Each sub-program corresponds to one TRAIL. The sub-program is executed with some inputs, and the outputs must all be sorted. A sub-program may be empty TRAIL 0. SMALL BLIND. This TRAIL requires you to sort all lists [a, b, c, d] where a < b > c < d TRAIL 1. BIG BLIND. This TRAIL requires you to sort all palindromes of length 5 FINAL TRAIL. This TRAIL requires you to sort a random list from the test cases. Muahahaha Also your sub-program will be run twice And then the sub-program is run on itself and must sort itself |
Written by firecubez
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # SLAY Specification ## SLAY Programs Programs consist of a sequence of bytes. ## Execution To execute, programs are hashed using the SHA3-512 algorithm. The next step depends on the architecture. On x86_64: - The resulting 64 byte array is XORed by the following byte array: `[182, 18, 146, 93, 235, 245, 110, 160, 61, 229, 102, 93, 222, 36, 70, 4, 240, 1, 32, 73, 205, 58, 249, 12, 229, 165, 212, 84, 66, 121, 185, 174, 150, 114, 145, 83, 200, 55, 190, 107, 182, 154, 30, 162, 176, 113, 56, 59, 64, 212, 77, 228, 146, 113, 70, 178, 97, 57, 34, 103, 220, 225, 38, 154]`. To XOR 2 arrays, each item `i` in `a` is set to `a[i] ^ b[i]`. - The result is interpreted as a x86_64 machine code function in the Microsoft x64 calling convention (even on other platforms) with 2 arguments. The first argument is a pointer to the input and the second argument is the length of the input. The signature is as follows: `void(char*, int)`. - This function is called with the proper arguments. On ARM64: - The resulting 64 byte array is XORed by the following byte array: `[10, 236, 147, 82, 232, 79, 111, 244, 28, 237, 39, 188, 219, 100, 204, 193, 117, 254, 101, 72, 32, 56, 188, 158, 10, 219, 88, 66, 232, 109, 248, 210, 132, 182, 149, 226, 219, 54, 66, 72, 192, 90, 39, 52, 202, 174, 253, 237, 163, 160, 159, 30, 32, 229, 214, 27, 208, 173, 178, 102, 115, 113, 178, 225]`. The method of XORing is described above. - The result is appended to a list C. - The result of the XOR is XORed again with the following byte array: `[222, 250, 255, 37, 168, 252, 255, 96, 225, 11, 95, 135, 4, 4, 0, 145, 132, 0, 1, 139, 225, 3, 0, 170, 37, 0, 128, 82, 34, 0, 64, 57, 35, 4, 64, 57, 95, 0, 3, 107, 137, 0, 0, 84, 5, 0, 128, 82, 35, 0, 0, 57, 34, 4, 0, 57, 33, 4, 0, 145, 63, 0, 4, 235]`. - The first twelve bytes of the result are appended to the end of list C. - The bytes of list C are interpreted as an ARM64 machine code function in the C calling convention with 2 arguments, as above. - This function is called with the proper arguments. After this, the (now modified) input is printed to standard output. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | use sha3::Digest; use std::{fs, env}; fn main() { let mut args = env::args(); args.next(); let program = fs::read(args.next().unwrap()).unwrap(); let mut input = args.next().unwrap() .split(",") .map(|x| x.parse::<u8>().unwrap()) .collect::<Vec<_>>(); let mut h = sha3::Sha3_512::new(); h.update(&program); let mut r = h.finalize(); let r = &mut r[..]; let carve; #[cfg(target_arch = "x86_64")] { x(r, &[182, 18, 146, 93, 235, 245, 110, 160, 61, 229, 102, 93, 222, 36, 70, 4, 240, 1, 32, 73, 205, 58, 249, 12, 229, 165, 212, 84, 66, 121, 185, 174, 150, 114, 145, 83, 200, 55, 190, 107, 182, 154, 30, 162, 176, 113, 56, 59, 64, 212, 77, 228, 146, 113, 70, 178, 97, 57, 34, 103, 220, 225, 38, 154]); let mut cartography = memmap2::MmapMut::map_anon(r.len()).unwrap(); cartography.copy_from_slice(r); carve = cartography.make_exec().unwrap(); unsafe { std::mem::transmute::<_, extern "win64" fn(*mut u8, u32)>(carve.as_ptr())(input.as_mut_ptr(), input.len() as u32); } }; #[cfg(target_arch = "arm64")] { x(r, &[10, 236, 147, 82, 232, 79, 111, 244, 28, 237, 39, 188, 219, 100, 204, 193, 117, 254, 101, 72, 32, 56, 188, 158, 10, 219, 88, 66, 232, 109, 248, 210, 132, 182, 149, 226, 219, 54, 66, 72, 192, 90, 39, 52, 202, 174, 253, 237, 163, 160, 159, 30, 32, 229, 214, 27, 208, 173, 178, 102, 115, 113, 178, 225]); let mut cartography = memmap2::MmapMut::map_anon(76).unwrap(); cartography[..64].copy_from_slice(r); x(r, &[222, 250, 255, 37, 168, 252, 255, 96, 225, 11, 95, 135, 4, 4, 0, 145, 132, 0, 1, 139, 225, 3, 0, 170, 37, 0, 128, 82, 34, 0, 64, 57, 35, 4, 64, 57, 95, 0, 3, 107, 137, 0, 0, 84, 5, 0, 128, 82, 35, 0, 0, 57, 34, 4, 0, 57, 33, 4, 0, 145, 63, 0, 4, 235]); cartography[64..].copy_from_slice(r[..12]); carve = cartography.make_exec().unwrap(); unsafe { std::mem::transmute::<_, extern "C" fn(*mut u8, u32)>(carve.as_ptr())(input.as_mut_ptr(), input.len() as u32); } } println!("{:?}", input); } fn x(s: &mut [u8], b: &[u8]) { for (a, b) in s.iter_mut().zip(b) { *a ^= *b; } } |
Written by Ardemit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | Psst. You can read a nicely formatted version of this document here -> https://esolangs.org/wiki/Rings Rings is a simple programming language designed around the idea of working with rotating memory strips. # Language overview The language revolves around turnable rings of varying length (or capacity if you want to picture them as all being the same size). Each ring can store up to 255 unsigned 8 bit integers (but cannot be zero-length) and can rotate counter-clockwise up to 255 steps at once (rotary right shift). == NB: Due to a bug in the interpreter, this is not accurate. See below. == All operations working with values stored on the rings only use the currently selected value, usually represented on a state diagram as being the leftmost value on the ring. Imagine looking at a vertical rod, on which the rings are stacked. You are the interpreter and can only see the values in front of you. If you want to access other ones, you need to rotate the rings around. There can be at most 256 rings. The language's interpreter represents a ring as a list of integers along with an index representing where the current value is. To rotate a ring of length L by N steps, the new value of the index should be (index+N)%L. However, there is a bug in the source code that causes this math to be done with integers that are too small. The end result is that the new value of the index becomes ((index+N)%256)%L instead. This causes rotations to act erroneously in some cases. It also means that the current value of the index, which should be a mere implementation detail, affects the result. ## Table of instructions Arguments are represented in the form <number of arguments><signed: i, unsigned: u><number of bits> – similar to Rust. | HumanRings Rings Arguments Description | mkr 0x0 1u8 Creates a new ring. The argument specifies the length. | put 0x1 2u8 The first argument specifies a ring, the second a literal value, that will be put onto said ring. | rot 0x2 2u8 The first argument specifies a ring, the second a literal value, that will tell the ring how many steps to rotate. | swp 0x3 2u8 Both arguments represent rings. The values of these rings will be swapped. | inp 0x4 1u8 Reads a value from stdin and puts it onto the ring specified by the argument. | out 0x5 1u8 Writes the value on the ring specified by the argument to stdout. | err 0x6 1u8 Writes the value on the ring specified by the argument to stderr. | add 0x7 3u8 All arguments represent rings. Add the values of the first two rings and put them onto the third. (A+B=C) | sub 0x8 3u8 All arguments represent rings. Subtract the second ring's value from the first ring's value and put them onto the third. (A-B=C) | mul 0x9 3u8 All arguments represent rings. Multiply the values of the first two rings and put them onto the third. (A*B=C) | div 0xA 3u8 All arguments represent rings. Divide the first ring's value by the second ring's value and put them onto the third. (A/B=C) | jmp 0xB 1u16 The argument is an instruction pointer, that the compiler unconditionally jumps to. | jeq 0xC 2u8 1u16 The first two arguments represent rings, the third an instruction pointer. If the ring's values are equal, jump to the pointer. (A=B) | jgt 0xD 2u8 1u16 The first two arguments represent rings, the third an instruction pointer. If the first ring's value is greater than the second ring's, jump to the pointer. (A>B) | jlt 0xE 2u8 1u16 The first two arguments represent rings, the third an instruction pointer. If the first ring's value is less than the second ring's, jump to the pointer. (A<B) | hlt 0xF 1u8 Halts the program, with the argument representing an exit code literal. Special behavior with the argument being 0xFE and 0xFF. ## The hlt instruction The hlt instruction has special behavior when it's argument is 0xFE (254) or 0xFF (255). In all other cases, the argument simply represents the program's exit code. The two special cases are used for debugging purposes and shouldn't be included in programs intended for release. When the compiler encounters either of these instructions, it prints the current Rings working environment to the default terminal, regardless of the intended method of handling stdout. Specific purpose interpreters can ignore this special behavior. The interpreter starts with the first ring (index 0) and continues up, printing one ring per line in the following format: 0x<ring index>: (+<ring rotation offset>)[<selected value>][<previous value>]<..>[<next value>] Example 'hlt 254' output at one point during the execution of a sorting program (all numbers are in hex): 0x00: (+00)[FF][FF][FF][D6][D8][DF][F3][FF][FF][FF][FF][FF][FF][FF][FF] 0x01: (+00)[08][00] 0x02: (+01)[0F][01][0C] 0x03: (+08)[00][D1][C3][A9][9E][94][89][5A][4B][00][00][00][00][00][00] The only difference between 'hlt 254' and 'hlt 255' is that 'hlt 255' halts and returns 255 as exit code, while 'hlt 254' continues with execution and is useful to debug loops for example. ## Source code structure The competition imposed several restrictions on the language, on of them being, that a program sorting a list of integers must be 100 bytes at a maximum. As such, readability has been sacrificed for compression. Rings uses '.rn' files as the source code. The entire file is in Big-endian. Since the instruction opcodes are only 4 bits, the first byte contains two instruction opcodes. The first 4 bits represent the instruction that is executed first (first instruction). The second instruction are the remaining 4 bits. instr1 = byte & 0b1111; instr2 = byte >> 4; Following are the arguments for the first instruction, then the second instruction. Since the interpreter knows how many arguments the instructions take, there is no terminator at the end of a block. Consider this Rings source code (each pair represents a byte): 10 08 00 05 Because programming directly in native Rings would be very difficult, there is a human-readable variant called HumanRings that compiles into native Rings source code. The code above can be represented in HumanRings as: mkr 8 put 0 5 ## HumanRings HumanRings is a dialect of Rings that is meant to be programmed in by humans and compiles into Rings source code that the interpreter can execute. A HumanRings compiler should be included standard with general purpose Rings interpreters. HumanRings programs should use the '.hrn' file extension, but the file is written in plain text, so '.txt' can also be used. In HumanRings, each line corresponds to a single instruction. Leading and trailing whitespaces are ignored, but arguments must be separated by a single regular space (U+0020). HumanRings use all the instructions from native Rings as seen in the table above, but assign a human-readable three-letter name to each. In addition, there is a HumanRings exclusive instruction that declares labels. In native Rings, jump instructions use 16 bit instruction pointers that represent a specific instruction index from the start of the file. If a programmer were to change the previous instructions, the pointer (and all the ones that follow) would start pointing towards wrong instructions. Instead of this system, HumanRings use labels to mark specific instructions and use them in jump instructions instead of absolute values. Labels are defined using the ':' (colon) character, followed by the name of the label. Names must not contain any whitespaces, so ':lbl_test' is a valid label, but ': lbl_test' or ':lbl test' are not. Jump instructions are then passed the label names (including the colon) instead of instruction pointers. Literal values are defined using prefixes. Hexadecimal values can use both uppercase and lowercase characters, but the prefix must be lowercase. | System Prefix Example Decimal value | Decimal 182 182 | Hexadecimal 0x 0xB6 182 | Octal 0 0266 182 | Binary 0b 0b10110110 182 Any lines beginning with the '#' character are considered comments and are ignored. Short HumanRings example program using all the rules described in this section: # Declare a ring for no reason and put 241 on it mkr 13 put 0 0xF1 # Still a comment. Leading and trailing whitespaces are removed! #Also, noone cares there isn't a space after the hashtag. # Infinite loop :go_here jmp :go_here This program compiles into the following Rings source code: 10 0D 00 F1 0B 00 02 ## Computational class Rings was meant to participate in the aforementioned competition. As such, there are several restrictions imposed upon it. One of them is, that it must be possible to write a program for sorting lists of integers in under 100 bytes. For this reason, Rings is internally limited to 8 bit ring addresses, 8 bit ring lengths and 8 bit unsigned values. If there weren't any of these restrictions and all of the aforementioned would be unbounded, Rings would be Turing complete. But since these restrictions are in place, Rings is much closer to being a linear bounded automaton. ## Notable problems * Rings doesn't have a straightforward way of handling EOF, because it only works with unsigned 8-bit integers and thus cannot distinguish what is a valid input and when EOF has been reached. In most cases, 0xFF should be considered EOF. * Rings has no error handling mechanism, so the programmer must take care to handle all possible problems before executing instructions. One of the most common problems are invalid arithmetic operations, that would put an invalid value onto a ring. ## Example programs All examples are written in HumanRings. ## Count 11 to 20 mkr 1 mkr 2 put 0 10 put 1 1 rot 1 1 put 1 20 :loop rot 1 1 add 0 1 0 out 0 rot 1 1 jlt 0 1 :loop ## Cat A simple cat program. Takes bytes until EOF (0xFF) and outputs them. mkr 1 mkr 1 put 1 0xFF :loop inp 0 out 0 jlt 0 1 :loop ## External resources The author, has created an interpreter for Rings, that can be found at https://github.com/marekmiklenda/Rings. |
1 | Rings is implemented at https://github.com/marekmiklenda/Rings. |
1 | The interpreter uses standard IO for both input and output. The qualification program takes 8 bit unsigned integers (15 at most, one per byte) terminated by 0xFF and outputs them in ascending order, terminated by 0xFF. |
Written by umnikos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | Hello there, I am pleased to offer you the postion of Junior UwUmanic Engineer at UwUmanics Technologies. You will be starting on June 26th 2022 as per our agreement. You have been assigned to work on one of our cutting edge uwuputers (uwumanic computer) that is still in active research and development. In case you are not familiar with those, they're a brand new type of computer that, unlike a quantum computer, can store an infinite amount of data in a single register! Our uwuputers have a whopping 8 registers to work with, more than those of any of our competitors. As you haven't listed expertise in uwumanics on your resume and you might need a tl;dr before jumping into the books, here's a quick rundown of everything known in the field: A single register can either store a single integer (unbounded, so that alone can store an infinite amount of information) or a list. A list of what? Nobody knows, as the lists are completely unindexable. As the lists can be compared we think the items inside the lists can also be compared to each other. That would also mean that they can be sorted, but no one has been able to demonstrate that. The only things that have been done are merging two lists into one (the uwu operation) and splitting a list into two (the manic operation). The details of those I will leave it to you to familiarize with. Please confirm your acceptance of this offer by signing and returning a copy of this letter to lyricly@uwumanics-tech.com We look forward to welcoming you on board. Sincerely, Alex Stefanov CEO UwUmanics Technologies |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | # name is short for "uwumanic computer" # # cheat sheet: # 8 registers # they can only store either a list of ??? or an integer (can be repurposed) # assembly-like syntax with destination commonly being the first argument # # note for the jump instruction: instead of labels it accepts a line number, # and the first line of the file is number 0. # blank lines and comments also count as lines. import sys def uwu(first, second): assert(type(first) is list) assert(type(second) is list) res = [] while True: if first == []: res += second return res if second == []: res += first return res if first[0] <= second[0]: res.append(first[0]) del first[0] else: res.append(second[0]) del second[0] def manic(integer, array): assert(type(array) is list) assert(type(integer) is int) if integer<0: array = array[::-1] integer *= -1 if integer > len(array): integer = len(array) return (array[:integer], array[integer:]) if __name__ == "__main__": filename = sys.argv[1] program = open(filename, 'r').read() lines = program.splitlines() instructions = [line.split() for line in lines] pc = 0 registers = {} registers_count = 8 for i in range(registers_count): # no uppercase for you. registers[chr(ord('a')+i)] = 0 def get(s): try: return int(s) except: return registers[s] while True: #print(registers) ins = instructions[pc] if ins == []: # blank line, ignore pc += 1 continue op = ins[0] # switch statements are only in python 3.10 and I have 3.9 #debianmoment if op == 'cmp' or op == 'compare' or op == '>': # works on arrays as well! a = registers[ins[1]] b = get(ins[2]) c = get(ins[3]) if b<c: registers[ins[1]] = -1 elif b>c: registers[ins[1]] = 1 else: registers[ins[1]] = 0 elif op == 'jmp' or op == 'jump' or op == 'j': if len(ins) == 2: # unconditional jump a = get(ins[1]) pc = a+0 elif len(ins) == 4: # conditional jump a = get(ins[1]) b = get(ins[2]) c = get(ins[3]) assert(type(a) is int) if a == 0: pc = b+0 else: pc = c+0 else: raise Exception("incorrect number of arguments to jump instruction") continue elif op == 'add' or op == '+': a = registers[ins[1]] b = get(ins[2]) registers[ins[1]] = a+b+0 elif op == 'neg' or op == 'negate': # no subtraction a = registers[ins[1]] registers[ins[1]] = 0-a elif op == 'mul' or op == '*' or op == 'multiply': a = registers[ins[1]] b = get(ins[2]) assert(type(a) is int) assert(type(b) is int) registers[ins[1]] = a*b elif op == 'hlt' or op == 'halt' or op == 'h': break elif op == '#' or op == '//' or op == '--' or op == ';' : # comment pass elif op == 'uwu' or op == '🥺': a = registers[ins[1]] b = registers[ins[2]] registers[ins[1]] = uwu(a,b) elif op == "manic" or op == "mnc": a = registers[ins[1]] b = registers[ins[2]] registers[ins[1]], registers[ins[2]] = manic(a,b) elif op == 'len' or op == 'length' or op == 'l': a = registers[ins[1]] b = registers[ins[2]] registers[ins[1]] = len(b) elif op == 'mov' or op == 'move' or op == 'copy' or op == "=": # it does indeed copy a = registers[ins[1]] b = get(ins[2]) registers[ins[1]] = b elif op == 'in' or op == 'inp' or op == 'read' or op == 'input' or op == 'i': a = registers[ins[1]] line = input() arr = list(map(int,line.split())) registers[ins[1]] = arr elif op == 'out' or op == 'print' or op == 'output' or op == 'o': a = registers[ins[1]] assert(type(a) is list) for item in a: print(f"{item} ", end="") print("") else: raise Exception("invalid instruction") pc += 1 |
Written by Palaiologos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | # Read the file passed in args to a string. import sys # Maximum amount of memory. Tweak if needed. MEM = 65536 with open(sys.argv[1], 'r') as f: code = f.read().split('\n') magic, code = int(code[0]), code[1] if len(code) > 105: print("Code too long.") exit() conversions = { 'R':'BBB8B25BGf', 'P':'558545C5F5535E', 'O':'FB04001058039BBB', 'N':'455E4D09988F87213E656060', 'M':'Gj369Gf825Gl471AGzGzGzGy', 'L':'12GeF0Hi5BBB8BGfHh4BBB7BBBA1GfBBB444Hg66BBB3BB9Gf8BBB888', 'K':'F73619D1253F15651565FE8F2F2E620B320F73E232357576A3B4342A', 'J':'10151512234334533535906165151510572322324454545824350353' } for key in conversions: code = code.replace(key, conversions[key]) new_code = '' i = 0 while i < len(code): if code[i] == 'G': new_code = new_code + "0" * (ord(code[i + 1]) - ord('a')) i = i + 1 elif code[i] == 'H': new_code = new_code + "B" * (ord(code[i + 1]) - ord('a')) i = i + 1 elif code[i] == 'I': new_code = new_code + "F" * (ord(code[i + 1]) - ord('a')) i = i + 1 else: new_code = new_code + code[i] i += 1 # The magic algorithm. T = [ord(c) for c in new_code] TL = len(T) C = [0] * 256 i = p = length = half = 0 A = [0] * TL for c in new_code: C[ord(c)] += 1 for c in range(256): p = C[c]; C[c] = i; i += p for i in range(magic): A[C[T[i]]] = i C[T[i]] += 1 for i in range(magic, TL): A[C[T[i]]] = i + 1 C[T[i]] += 1 p = magic for i in range(TL): c = 0 length = 256 half = length >> 1 while 0 < length: # ... if C[c + half] < p: c += half + 1 half -= (length & 1) ^ 1 length = half half = half >> 1 T[i] = c p = A[p - 1] def brk(s, n): return [s[i:i+n] for i in range(0, len(s), n)] def convert(s): num = int(s, 16) if num > 2048: return num - 4096 else: return num a = list(map(convert, brk(''.join([chr(c) for c in T]), 3))) a += [0] * (MEM - len(a)) i = 0 while i >= 0: if a[i] == -1: a[a[i + 1]] = int(input()) elif a[i + 1] == -1: print(str(a[a[i]]) + " ", end="") else: a[a[i + 1]] -= a[a[i]] if a[a[i + 1]] <= 0: i = a[i + 2] continue i += 3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | # XY Machine The program is a file with exactly two lines. The first line is a decimal number (subsequently called the _magic_ number) and the second line is the code. The code may not exceed 105 octets, or an error message will be printed and the interpreter will terminate. First, the following substitutions are made on the code line: ``` R => BBB8B25BGf P => 558545C5F5535E O => FB04001058039BBB N => 455E4D09988F87213E656060 M => Gj369Gf825Gl471AGzGzGzGy L => 12GeF0Hi5BBB8BGfHh4BBB7BBBA1GfBBB444Hg66BBB3BB9Gf8BBB888 K => F73619D1253F15651565FE8F2F2E620B320F73E232357576A3B4342A J => 10151512234334533535906165151510572322324454545824350353 ``` Then, every instance of the letter G followed by a lowercase letter is replaced with the amount of zeroes corresponding to the position of the letter in the lowercase alphabet (0-indexed). For example, `Gg` becomes `000000`. The same rule holds for the uppercase letters H and I, except H produces `B`s and I produces `F`s, so for example `Ig` becomes `FFFFFF`. After these transformations are performed, the code becomes a a sequence of hexadecimal digits. Then, the magic algorithm of the code is performed. The magic "encryption" algorithm steps follow: - Consider T the array of ASCII codepoints of the input. Consider TL the length of it. - Create an array called C with 256 zeroes. - Create an array called A with TL zeroes. - For each element E of the T array, increment the E-th cell of C. - Given I=0, for each number N in range `<0, 256)`, set the N-th element of C to I and increment I by the previous value of the N-th element of C. - For each I in `<0, magic number)`, take the I-th element of T and call it X. Use X to index into C and then A. Set the value in the A array to I. Increment X-th element of C. - For each I in `<magic number, TL)`, take the I-th element of T and call it X. Use X to index into C and then A. Set the value in the A array to I + 1. Increment X-th element of C. - For each I in range `<0, TL)`: - Consider c = 0, length = 256, half = 128. - While length is non-zero: - If the (c+half)-th element of C is smaller than the magic number, add half+1 to c and subtract the inverted least significant bit of length from half. - Set length to half and halve half. - Set the I-th element of T to c. - Set take the (magic number - 1)-th element of the A array. Make it the new magic number. - Turn every element of the T array to a character value and concatenate them all to return a code string. Then, break up the code string into three byte-long pieces. Then, interpret every three byte long piece as a hexadecimal number. If the number is greater than 2048, subtract 4096 from it. Finally, yield it. This process will turn the code into an array of signed integers. Then, pad the resulting array (call it the memory) with zeroes to MEM entries (implementation defined, the supplied interpreter assumes MEM=65536). Then enter the evaluation loop. Assuming, initially, i=0: - if memory[i] = -1: set memory[memory[i + 1]] to the number read from standard input. - if memory[i + 1] = -1: print memory[memory[i]] as a number to the standard output. - Otherwise, subtract in-place memory[memory[i]] from memory[memory[i + 1]]. If the value of memory[memory[i + 1]] becomes less than or equal to 0, set i=memory[i + 2] and enter the next iteration of the evaluation loop. - Finally, increment i by 3. |
1 | Reads a number N, then reads N numbers comprising the list and writes them in sorted order. |
Written by RocketRace
1 | (C)((S)((S)(Z)))((C)((S)(Z))(E)) |
1 | Given the definitions `NZ|SN` and `LE|CNL`, the input and output are given as linked lists of Peano numerals. See `example-input`0span>. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #/bin/bash if [ "$#" -ne 2 ]; then echo "usage: $0 source input" >&2 exit 1 fi if ! [ -e "$1" ]; then echo "$1: not found" >&2 exit 1 fi if ! [ -e "$2" ]; then echo "$2: not found" >&2 exit 1 fi cat $2 | python3 -c "$(cat $1 | python3 ./tflite.py)" |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | # TFLite A Tiny Functional Language (lite). ## Declarations Newlines and semicolons delimit. Leading characters name. ## ADTs Uppercase names define ADTs. Bar denotes sums, juxtaposition products. * `FA|BF|CFF` <=> `data F = A | B F | C F F` ## Functions Lowercase names are functions. Numbers denote arity. An expression follows arguments. * `k2xyx` <=> `k x y = x` ## Pattern matching Argments are matched structurally. Uppercase destructures ADTs. Lowercase binds. Underscore ignores. Bar delimits patterns. * `BT|F;a2TTT|Txx|_TF` <=> `data B = T | F`; `a T T = T`; `a T x = x`; `a _ T = F` ## Expressions Juxtaposition applies. Parentheses group. Brackets form anonymous functions. * `[1Sxx|ZSZ](S(SZ))` is `(SZ)` is `SZ` ## Main Caret denotes an entry point. * `^1xx` <=> `main x = x` ## I/O Input and output are shell standard. Both form expressions of ADTs. Newlines delimit inputs. Output is singular. Equivalence is expressionwise rather than bytewise. * `S(SZ)\nSZ` ~> `^2SxSy^xy|ZSxx|SxZx|ZZZ` ~> `SZ` is `S Z` is `S(Z)` is `(S)(Z)` is ... ## Whitespace Spaces and tabs are insignificant. Empty declarations vanish. * ` f 3g x y gy x ; ;;;;` is `f3gxygyx` ## Legal The rules disallow disallowing exploiting transpiler bugs. Exploits are discouraged. All material is served as is. All rights are reserved. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | # Many qualities of this transpiler are inspired by V import textwrap, sys def pattern(rest, datas): name = rest[0] rest = rest[1:] if name.isupper(): out = [name] for _ in range(datas[name]): pat, rest = pattern(rest, datas) out.append(pat) return out, rest elif name.islower(): return name, rest elif name == "_": return None, rest else: raise SyntaxError def expr(rest, datas, stack): call = [] while rest: c = rest[0] rest = rest[1:] match c: case ")": assert stack.pop() == "(" break case "]": assert stack.pop() == "[" break case "|": rest = "|" + rest break case "(": stack.append("(") e, rest = expr(rest, datas, stack) call.append(e) case "[": stack.append("[") f, rest = func(rest, datas, stack) call.append(f) case _: call.append(c) return call, rest def func(rest, datas, stack): count = int(rest[0]) return branch(rest[1:], datas, count, stack) def branch(rest, datas, count, stack): branches = [] start = True while start or (rest and rest[0] == "|"): if not start: rest = rest[1:] patterns = [] for _ in range(count): p, rest = pattern(rest, datas) patterns.append(p) e, rest = expr(rest, datas, stack) branches.append((patterns, e)) start = False return {'':branches}, rest def transpile(code): if len(code) > 100: raise SystemExit("golf it!") lines = sorted(filter(None, code.replace("\n",";").replace(" ","").replace("\t","").split(";"))) datas = {} fns = {} for line in lines: name = line[0] rest = line[1:] if name.isupper(): for branch in rest.split("|"): tag = branch[0] count = len(branch) - 1 datas[tag] = count else: if name == "^": name = "main" fn, rest = func(rest, datas, []) fns[name] = fn return write_header() + write_datas(datas) + write_fns(fns) + write_footer(datas) def write_header(): return textwrap.dedent(''' # generated by the TFLite transpiler version {} import enum, re class Data(list): # todo: static typing def __call__(self, arg): return Data(self + [arg]) def output(value): if isinstance(value, Data): if len(value) == 1: return output(value[0]) return "".join(f"({output(x)})" for x in value) elif isinstance(value, _data): return value.name return value.__name__ '''[1:]) def write_datas(datas): members = "\n".join(f" {c}=object()" for c in datas) if datas else " pass" kind = f"class _data(enum.Enum):\n{members}\n" constants = "\n".join(f"{c}=Data([_data.{c}])" for c in datas) return kind + constants + "\n" def write_fns(fns): return "\n".join(write_fn(name, fn) for name, fn in fns.items()) def write_pattern(pattern): return f"[{', '.join('_' if p is None else (f'_data.{p}' if p.isupper() else p) if isinstance(p, str) else write_pattern(p) for p in pattern)}]" def find_anons(expr, anons): for e in expr: if isinstance(e, dict): anons.append(e) for (_, branch) in e['']: find_anons(branch, anons) elif isinstance(e, list): find_anons(e, anons) return anons def write_expr(expr, anons, ids): out = "" for e in expr: e = "main" if e == "^" else e if isinstance(e, str): out+=f"({e})" elif isinstance(e, list): out +=f"({write_expr(e, anons, ids)})" elif isinstance(e, dict): out +=f"(anon_{ids[anons.index(e)]})" return out def write_fn(name, fn): fn = fn[''] out = "" n = len(fn[0][0]) if n == 0: expr = fn[0][1] anons = find_anons(expr, []) ids = [(abs(hash(str((i, anon, fn))))) for i, anon in enumerate(anons)] for id, anon in zip(ids, anons): out += textwrap.indent(write_fn(f"anon_{id}", anon), n*' ') out += f"{name}={write_expr(expr, anons, ids)}\n" else: for i in range(n):out+=f"{i*' '}def {name}{i*'_'}(_{i}):\n" out+=f"{n*' '}match [{', '.join(f'_{i}'for i in range(n))}]:\n" for pattern, expr in fn: out += f"{(n + 1)*' '}case {write_pattern(pattern)}:\n" anons = find_anons(expr, []) ids = [(abs(hash(str((i, anon, fn))))) for i, anon in enumerate(anons)] for id, anon in zip(ids, anons): out += textwrap.indent(write_fn(f"anon_{id}", anon), (n + 2) * ' ') out += f"{(n +2)*' '}return {write_expr(expr, anons, ids)}\n" out += f"{(n + 1)*' '}case _: raise RuntimeError('Bad arguments')\n" for i in range(n - 1): out += f"{(n-i-1)*' '}return {name}{(n-i-1)*'_'}\n" return out def write_footer(datas): chars = "".join(datas) return textwrap.dedent(f''' if __name__ == "__main__": args = [] for arg in open(0): arg = re.sub("\\\\s", "", arg) assert len(re.findall("[\\\\(\\\\){chars}]", arg)) == len(arg), "Bad character" assert not re.findall("\\\\(\\\\)", arg), "Bad format" try: args.append(eval(arg)) except SyntaxError as e: raise SyntaxError("Bad format") from e except Exception as e: raise RuntimeError("Bad input") from e try: value = main for arg in args: value = value(arg) print(output(value)) except Exception as e: raise RuntimeError("Bad program") from e '''[1:]) print(transpile(sys.stdin.read())) |