├── KleinException.py ├── helloworld.??? ├── Stack.py ├── LICENSE ├── README.md ├── lost.py └── interpreter.py /KleinException.py: -------------------------------------------------------------------------------- 1 | class KleinException(Exception): 2 | pass 3 | -------------------------------------------------------------------------------- /helloworld.???: -------------------------------------------------------------------------------- 1 | v<<<<<<<<<<<>>>>>>>>>>>> 2 | >%?!|"Hello, WorldvU"-+@ 3 | v 4 | -------------------------------------------------------------------------------- /Stack.py: -------------------------------------------------------------------------------- 1 | class Stack(list): 2 | def __getitem__(self,key): 3 | try: 4 | return super(Stack, self).__getitem__(key) 5 | except IndexError: 6 | return 0 7 | def pop(self): 8 | try: 9 | return super(Stack, self).pop() 10 | except: 11 | return 0 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lost 2 | 3 | Lost is a 2 dimensional language in which the start location and direction are 4 | entirely random 5 | 6 | # Memory 7 | 8 | The memory is stored in a stack and a scope. Both are stacks padded with zeros at the bottom. 9 | At the end of execution the contents of the stack are printed. 10 | 11 | Unlike most 2D languages the ip may start in any location moving in any direction. When the ip reaches an edge it will wrap to the other side (like a torus). 12 | 13 | # Commands 14 | 15 | ## Mirrors 16 | 17 | - `\` Swaps the x and y directions 18 | 19 | - `/` Swaps the x and y directions and multiplies them by -1 20 | 21 | - `|` Multiplies the horizontal direction by -1 22 | 23 | ## Directions 24 | 25 | - `>` Tells the ip to move east 26 | 27 | - `<` Tells the ip to move west 28 | 29 | - `v` Tells the ip to move south 30 | 31 | - `^` Tells the ip to move north 32 | 33 | ## Doors 34 | 35 | - `[` Reflects the ip if it is moving east; becomes `]` if the ip is moving horizontally 36 | 37 | - `]` Reflects the ip if it is moving west; becomes `[` if the ip is moving horizontally 38 | 39 | ## Jumps 40 | 41 | - `!` Skips the next operation 42 | 43 | - `?` Pops off the top of the stack and jumps if not zero 44 | 45 | ## Stack manipulation 46 | 47 | - `:` Duplicates the top of the stack 48 | 49 | - `$` Swaps the top two items of the stack 50 | 51 | - `(` Pops from the stack and pushes to the scope 52 | 53 | - `)` Pops from the scope and pushes to the stack 54 | 55 | ## Literals 56 | 57 | - `0`-`9` pushes n to the top of the stack 58 | 59 | - `"` Starts and ends a string literal. During a string literal commands are not run and instead their character values are pushed to the stack. 60 | 61 | ## Operations 62 | 63 | - `+` Adds the top two numbers 64 | 65 | - `*` Multiplies the top two numbers 66 | 67 | - `-` Multiplies the top by -1 68 | 69 | ## Control 70 | 71 | - `%` Turns the safety off 72 | 73 | - `#` Turns the safety on 74 | 75 | - `@` Ends execution if the safety is off (starts on) 76 | -------------------------------------------------------------------------------- /lost.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from interpreter import Interpreter 3 | from KleinException import KleinException 4 | 5 | parser = argparse.ArgumentParser(description="Klein Interpreter") 6 | 7 | parser.add_argument( 8 | "-v", 9 | "--version", 10 | action = "version", 11 | version = "%(prog)s 0.0", 12 | help = "Prints the version number of the interpreter." 13 | ) 14 | parser.add_argument( 15 | "-V", 16 | "--verify", 17 | action = "store_true", 18 | help = "Runs the interpreter in verification mode." 19 | ) 20 | 21 | parser.add_argument( 22 | "-Q", 23 | "--quiet-verification", 24 | action = "store_true", 25 | help = "Runs in a quieter version of verification mode." 26 | ) 27 | 28 | parser.add_argument( 29 | "-d", 30 | "--debug", 31 | action = "store_true", 32 | help = "Runs the interpreter in debug mode." 33 | ) 34 | 35 | parser.add_argument( 36 | "-a", 37 | "--ASCII-in", 38 | action = "store_true", 39 | help = "Takes input as ASCII code points" 40 | ) 41 | 42 | parser.add_argument( 43 | "-A", 44 | "--ASCII-out", 45 | action = "store_true", 46 | help = "Outputs as ASCII code points" 47 | ) 48 | 49 | parser.add_argument( 50 | "-c", 51 | "--ASCII", 52 | action = "store_true", 53 | help = "Takes input and outputs by ASCII code points" 54 | ) 55 | 56 | parser.add_argument( 57 | "source", 58 | metavar = "Source", 59 | help = "The name of the file from which the source is read." 60 | ) 61 | 62 | parser.add_argument( 63 | "input", 64 | metavar = "Input", 65 | nargs = "*", 66 | help = "Integer input." 67 | ) 68 | 69 | args = parser.parse_args() 70 | 71 | with open(args.source) as file: 72 | source = file.read() 73 | 74 | if args.ASCII or args.ASCII_in: 75 | a=Interpreter(source,map(ord," ".join(args.input))) 76 | else: 77 | a=Interpreter(source,map(int,args.input)) 78 | 79 | if args.debug: 80 | curselib = None 81 | try: 82 | import curses 83 | curselib = curses 84 | except ImportError: 85 | try: 86 | import unicurses 87 | curselib = unicurses 88 | except ImportError: 89 | raise KleinException("Cannot use debug mode without a curses library. Try installing either curses or unicurses.") 90 | screen = curselib.initscr() 91 | curselib.start_color() 92 | curselib.use_default_colors() 93 | curselib.init_pair(1, curselib.COLOR_RED, -1) 94 | while a.direction != [0,0]: 95 | a.output(screen,0,0) 96 | try: 97 | screen.addstr(a.dim,0," ".join(map(str,a.memory))) 98 | except:pass 99 | screen.refresh() 100 | screen.getch() 101 | try: 102 | screen.addstr(a.dim,0," "*len(" ".join(map(str,a.memory)))) 103 | except:pass 104 | a.action() 105 | a.move() 106 | curselib.endwin() 107 | 108 | elif args.verify or args.quiet_verification: 109 | 110 | maxX = len(source.strip().split("\n")) 111 | maxY = max(map(len,source.strip().split("\n"))) 112 | 113 | outputs = [] 114 | 115 | for x in range(maxX): 116 | for y in range(maxY): 117 | for z in [[1,0],[0,1],[-1,0],[0,-1]]: 118 | if args.ASCII or args.ASCII_in: 119 | a=Interpreter(source,map(ord," ".join(args.input)),x,y,z) 120 | else: 121 | a=Interpreter(source,map(int,args.input),x,y,z) 122 | 123 | while a.direction != [0,0]: 124 | a.action() 125 | a.move() 126 | if not args.quiet_verification: 127 | print (x,y,z),':', 128 | 129 | if args.ASCII or args.ASCII_out: 130 | o = "".join(map(chr,a.memory)) 131 | 132 | else: 133 | o = " ".join(map(str,a.memory)) 134 | outputs.append(o) 135 | print o 136 | 137 | if len(set(outputs)) == 1: 138 | print "Deterministic" 139 | else: 140 | print "Non-deterministic" 141 | 142 | 143 | else: 144 | while a.direction != [0,0]: 145 | a.action() 146 | a.move() 147 | 148 | if args.ASCII or args.ASCII_out: 149 | print "".join(map(chr,a.memory)) 150 | else: 151 | print " ".join(map(str,a.memory)) 152 | -------------------------------------------------------------------------------- /interpreter.py: -------------------------------------------------------------------------------- 1 | from Stack import Stack 2 | from random import randint 3 | 4 | class Interpreter(object): 5 | def __init__(self,source,input,startx=None,starty=None,dir=None): 6 | source = source.strip().split("\n") 7 | dim = max(map(len,source)+[len(source)]) 8 | self.source = [list(x.ljust(dim,"."))for x in source] 9 | self.dim = (len(self.source),len(self.source[0])) 10 | if dir == None: 11 | self.direction = [[1,0],[0,1],[-1,0],[0,-1]][randint(0,3)] 12 | else: 13 | self.direction = dir 14 | if (startx,starty) == (None,None): 15 | self.location = [randint(0,self.dim[0]-1),randint(0,self.dim[1]-1)] 16 | else: 17 | self.location = [startx,starty] 18 | self.memory = Stack(input) 19 | self.scope = Stack() 20 | self.read = False 21 | self.safety = False 22 | def wrapAround(self): 23 | self.location[0] %= self.dim[0] 24 | self.location[1] %= self.dim[1] 25 | def move(self): 26 | self.location = [ 27 | self.location[0]+self.direction[0], 28 | self.location[1]+self.direction[1] 29 | ] 30 | #Important bit 31 | if self.location[0] < 0: 32 | self.wrapAround() 33 | if self.location[1] < 0: 34 | self.wrapAround() 35 | if self.location[0] >= self.dim[0]: 36 | self.wrapAround() 37 | if self.location[1] >= self.dim[1]: 38 | self.wrapAround() 39 | def character(self): 40 | return self.source[self.location[0]][self.location[1]] 41 | def action(self): 42 | if self.read: 43 | if self.character() == '"': 44 | self.read = False 45 | else: 46 | self.memory.append(ord(self.character())) 47 | elif self.character() == "/": 48 | self.direction = map(lambda x:-x,self.direction[::-1]) 49 | elif self.character() == "\\": 50 | self.direction = self.direction[::-1] 51 | elif self.character() == "|": 52 | self.direction[1] *= -1 53 | elif self.character() == ">": 54 | self.direction = [0,1] 55 | elif self.character() == "<": 56 | self.direction = [0,-1] 57 | elif self.character() == "v": 58 | self.direction = [1,0] 59 | elif self.character() == "^": 60 | self.direction = [-1,0] 61 | elif self.character() == "%": 62 | self.safety = True 63 | elif self.character() == "#": 64 | self.safety = False 65 | elif self.character() == "@": 66 | if self.safety: 67 | self.direction = [0,0] 68 | elif self.character() == "[": 69 | if self.direction[1] == 1: 70 | self.direction[1] = -1 71 | if self.direction[1]: 72 | self.source[self.location[0]][self.location[1]] = "]" 73 | elif self.character() == "]": 74 | if self.direction[1] == -1: 75 | self.direction[1] = 1 76 | if self.direction[1]: 77 | self.source[self.location[0]][self.location[1]] = "[" 78 | elif self.character() in "0123456879": 79 | self.memory.append(int(self.character())) 80 | elif self.character() == "+": 81 | self.memory.append(self.memory.pop()+self.memory.pop()) 82 | elif self.character() == "*": 83 | self.memory.append(self.memory.pop()*self.memory.pop()) 84 | elif self.character() == "-": 85 | self.memory.append(-self.memory.pop()) 86 | elif self.character() == ":": 87 | self.memory.append(self.memory[-1]) 88 | elif self.character() == "$": 89 | a,b=self.memory.pop(),self.memory.pop() 90 | self.memory.append(a) 91 | self.memory.append(b) 92 | elif self.character() == "!": 93 | self.move() 94 | elif self.character() == "?": 95 | if self.memory.pop(): 96 | self.move() 97 | elif self.character() == "(": 98 | self.scope.append(self.memory.pop()) 99 | elif self.character() == ")": 100 | self.memory.append(self.scope.pop()) 101 | elif self.character() == '"': 102 | self.read = True 103 | def output(self,screen,a,b): 104 | try: 105 | import curses 106 | curselib = curses 107 | except ImportError: 108 | import unicurses 109 | curselib = unicurses 110 | 111 | for x in range(self.dim[0]): 112 | for y in range(self.dim[1]): 113 | try: 114 | if [x,y] == self.location: 115 | if curselib.has_colors(): 116 | screen.addstr(a+x,b+y*2,"X",curselib.color_pair(1)) 117 | else: 118 | screen.addstr(a+x,b+y*2,"X") 119 | else: 120 | screen.addstr(a+x,b+y*2,self.source[x][y]) 121 | except:pass 122 | --------------------------------------------------------------------------------