├── README.md └── interpreter ├── __pycache__ ├── loader.cpython-33.pyc ├── main.cpython-33.pyc └── stack.cpython-33.pyc ├── loader.py ├── main.py ├── scripts └── main.txt └── stack.py /README.md: -------------------------------------------------------------------------------- 1 | interpreter 2 | =========== 3 | 4 | interpreter written in python for a stack based language that uses reverse polish notation. 5 | 6 | you can call another file that shares the same stack with @ 7 | 8 | if, for, while statements can call another file as well. 9 | 10 | variables the can be named, fetched and stored 11 | 12 | hello world example: 13 | 14 | :hello :world .. echo 15 | 16 | :4 - adds 4 to the stack 17 | 18 | :hello - adds hello to the stack 19 | 20 | $hello - fetches the variable hello and adds it to the stack 21 | 22 | =hello - set hello to a value from the stack 23 | 24 | @hello - run the file with the name hello 25 | 26 | instructions: 27 | 28 | echo - prints the last stack item to the console 29 | 30 | in - pushes user input 31 | 32 | rnd - pops 2 numbers, they specify the random number's min and max values then a random number is added to the stack 33 | 34 | info - prints debugging info to the screen 35 | 36 | math instructions: 37 | 38 | plus - pops 2 numbers, adds them together then pushes the result 39 | 40 | minus - pops 2 numbers, subtracts the right number from the left then pushes the result 41 | 42 | multiply - pops 2 numbers, multiplies them together then pushes the result 43 | 44 | divide - pops 2 numbers, divides the left number by the right then pushes the result 45 | 46 | % - pops 2 numbers, pushes the remainder of the left number being divided by the right 47 | 48 | ** - pops 2 numbers, rises the left number by the right then pushes the result 49 | 50 | or - if either of the values are true it evaluatates to true 51 | 52 | ! - turns a positive number negative and a negative number positive 53 | 54 | sqrt - pops a number, pushes the square root of the number 55 | 56 | cos - pops a number, pushes the cosine of the number 57 | 58 | sin - pops a number, pushes the sine of the number 59 | 60 | tan - pops a number, pushes the tangent of the number 61 | 62 | dist - pops 4 numbers, x0 y0 x1 y1 pushes the distance between the sets of coordinates 63 | 64 | string instructions: 65 | 66 | . - pops 2 strings, joins them together then pushes the result 67 | 68 | 69 | sub - pops 3 strins, pushes the result of a substition regex 70 | 71 | rnd_str - pops 1 number, pushes a random string of that length 72 | 73 | starts - pops 2 strings, if the first string starts with the second string then it pushes true otherwise it pushes false 74 | 75 | ends - pops 2 strings, if the first string ends with the second string then it pushes true otherwise it pushes false 76 | 77 | basic instructions: 78 | 79 | dup - duplicates the last stack item by adding another to the stack 80 | 81 | swap - swaps the last 2 values in the stack 82 | 83 | equality instructions: 84 | 85 | == - pops 2 values, pushes True onto the stack if the values are equal, else pushes False 86 | 87 | != - pops 2 values, pushes True onto the stack if the values are not equal, else pushes False 88 | 89 | < - pops 2 values, pushes True if the left value is less than the right, else pushes False 90 | 91 | > - pops 2 values, pushes True if the left value is greater than the right, else pushes False 92 | 93 | <= - pops 2 values, pushes True if the left value is less than or equal to the right, else pushes False 94 | 95 | >= - pops 2 values, pushes True if the left value is greater than or equal to the right, else pushes False 96 | 97 | looping instructions: 98 | 99 | repeat - pops 2 values, the 1st is the amount, the 2nd is the function that will be looped by that amount 100 | 101 | for - pops 3 values, the 1st is the memory index of the variable, the 2nd is the number to go to, the 3rd is the address to be looped over, increments variable until its greater or equal to the end variable 102 | 103 | while - pops 2 values, the 1st is the memory index of the variable when it is false the loop ends, the 2nd is the file to be looped over 104 | 105 | branching instructions: 106 | 107 | if - pops 2 values, the first is the condition, if it is true the 2nd value is used as an address of a file to be called 108 | 109 | built in memory values: 110 | 111 | $true - True 112 | 113 | $false - False 114 | 115 | $pi - 3.14... 116 | 117 | $s - ' ' space to be concatenated onto strings 118 | 119 | used to write code: 120 | 121 | $: - 'literal' 122 | 123 | $@ - 'file_call' 124 | 125 | $$ = 'fetch' 126 | 127 | $= - 'store' 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /interpreter/__pycache__/loader.cpython-33.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jake100/interpreter/b609af7778b6bf944414a26bcdef9a1a37b31c81/interpreter/__pycache__/loader.cpython-33.pyc -------------------------------------------------------------------------------- /interpreter/__pycache__/main.cpython-33.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jake100/interpreter/b609af7778b6bf944414a26bcdef9a1a37b31c81/interpreter/__pycache__/main.cpython-33.pyc -------------------------------------------------------------------------------- /interpreter/__pycache__/stack.cpython-33.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jake100/interpreter/b609af7778b6bf944414a26bcdef9a1a37b31c81/interpreter/__pycache__/stack.cpython-33.pyc -------------------------------------------------------------------------------- /interpreter/loader.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | def substitute_tokens(orig_tokens): 4 | tokens = [] 5 | for i, token in enumerate(orig_tokens): 6 | if i == 0 or orig_tokens[i-1] != 'string': 7 | try: 8 | int(token) 9 | tokens.append('number') 10 | except ValueError: 11 | try: 12 | float(token) 13 | tokens.append('number') 14 | except ValueError: pass 15 | if token.startswith('='): 16 | tokens.append('store') 17 | token = token[1:] 18 | if token.startswith('$'): 19 | tokens.append('fetch') 20 | token = token[1:] 21 | if token.startswith('->'): 22 | tokens.append('func_call') 23 | token = token[2:] 24 | if token.startswith('@'): 25 | tokens.append('file_call') 26 | token = token[1:] 27 | if token.startswith('&'): 28 | tokens.append('get_func') 29 | token = token[1:] 30 | if token == 'and': token = 'and_op' 31 | if token == 'time': token = 'get_time' 32 | if token == '++': token = 'increment' 33 | if token == '--': token = 'decrement' 34 | if token == '...': token = 'join_new_line' 35 | if token == '..': token = 'join' 36 | if token == '.': token = 'concat' 37 | if token == 'sqrt': token = 'square_root' 38 | if token == '**': token = 'power' 39 | if token == '%': token = 'modulus' 40 | if token == '+': token = 'plus' 41 | if token == 'if_else': token = 'if_else_branch' 42 | if token == 'if': token = 'if_branch' 43 | if token == 'for': token = 'for_op' 44 | if token == 'while': token = 'while_op' 45 | if token == 'input': token = 'get_input' 46 | if token == '-': token = 'minus' 47 | if token == '*': token = 'star' 48 | if token == '/': token = 'slash' 49 | if token == '|': token = 'orOp' 50 | if token == '!': token = 'negate' 51 | if token == '==': token = 'equals' 52 | if token == '!=': token = 'notequals' 53 | if token == '<': token = 'less_than' 54 | if token == '>': token = 'greater_than' 55 | if token == '<=': token = 'less_or_equal' 56 | if token == '>=': token = 'greater_or_equal' 57 | tokens.append(token) 58 | return tokens 59 | def tokenize(lines): 60 | tokens = [] 61 | strings = [] 62 | functions = {} 63 | new_lines = '' 64 | for i, line in enumerate(lines): 65 | line = re.sub(r'#.*$', "", line) 66 | line = re.sub('\n', ' ', line) 67 | line = re.sub('\t', '', line) 68 | line = re.split('\'', line) 69 | for j, c in enumerate(line): 70 | if j % 2 == 0: 71 | new_lines += c 72 | else: 73 | strings.append(c) 74 | new_lines += 'string ' + str(len(strings) - 1) 75 | new_lines = re.split(';', new_lines) 76 | for i, token in enumerate(new_lines): 77 | if token != '' and token != ' ' and token != '\t': 78 | token = token.strip() 79 | token = re.split(' ', token) 80 | if i % 2 != 0: 81 | functions[token[0]] = token[1:] 82 | else: 83 | tokens += token 84 | tokens = substitute_tokens(tokens) 85 | return [tokens, strings, functions] 86 | class FileHandler: 87 | def __init__(self): 88 | self.files = {} 89 | __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__), 'scripts')) 90 | for root, dirs, all_files in os.walk(os.path.dirname(__file__)): 91 | for file in all_files: 92 | if file.endswith('.txt'): 93 | self.files[os.path.splitext(file)[0]] = File(os.path.splitext(file)[0]) 94 | class File: 95 | tokens = [] 96 | strings = [] 97 | def __init__(self, name): 98 | self.name = name 99 | self.tokens = [] 100 | self.strings = [] 101 | self.functions = [] 102 | __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__), 'scripts')) 103 | path = os.path.join(__location__, name + '.txt') 104 | fo = open(path, "r") 105 | lines = fo.readlines() 106 | fo.close() 107 | p = tokenize(lines) 108 | self.tokens = p[0] 109 | self.strings = p[1] 110 | self.functions = p[2] 111 | fh = FileHandler() 112 | -------------------------------------------------------------------------------- /interpreter/main.py: -------------------------------------------------------------------------------- 1 | import cProfile 2 | import random 3 | import stack 4 | import parser 5 | import loader 6 | def main(): 7 | print('starting...') 8 | main = loader.fh.files['main'] 9 | print('loading...') 10 | print(main.tokens, main.strings, main.functions) 11 | st = stack.Stack(main.tokens, main.strings, main.functions) 12 | print('done...') 13 | if __name__ == '__main__': 14 | main() #cProfile.run('main()') 15 | -------------------------------------------------------------------------------- /interpreter/scripts/main.txt: -------------------------------------------------------------------------------- 1 | '' =str 2 | 'i' 0 100 &fizz_buzz for 3 | $str echo 4 | ; fizz_buzz 5 | $i 3 % 0 == 6 | $i 5 % 0 == 7 | and &both &one if_else 8 | ; 9 | 10 | ; both 11 | $str 'fizz buzz, ' . =str 12 | ; 13 | 14 | ; one 15 | $i 3 % 0 == 16 | &fizz if 17 | $i 5 % 0 == 18 | &buzz if 19 | ; 20 | 21 | ; fizz 22 | $str 'fizz, ' . =str 23 | ; 24 | 25 | ; buzz 26 | $str 'buzz, ' . =str 27 | ; -------------------------------------------------------------------------------- /interpreter/stack.py: -------------------------------------------------------------------------------- 1 | import parser 2 | import main 3 | import random 4 | import math 5 | import re 6 | import os 7 | import loader 8 | import string 9 | import time 10 | def is_num(s): 11 | try: 12 | int(s) 13 | return True 14 | except ValueError: 15 | return False 16 | def num(s): 17 | try: 18 | return int(s) 19 | except ValueError: 20 | try: 21 | return float(s) 22 | except ValueError: 23 | return 0 24 | class Stack: 25 | s = [] 26 | #mem holds global variables 27 | mem = {'true': True, 'false': False, 'pi': math.pi} 28 | 29 | code = [] 30 | pntr = 0 31 | def __init__(self, code, strings, functions): 32 | self.code = code 33 | self.pntr = 0 34 | self.strings = strings 35 | self.functions = dict(functions) 36 | while self.pntr < len(self.code): 37 | for i in self.inst: 38 | if i.__name__ == self.code[self.pntr]: 39 | i(self) 40 | self.info() 41 | break 42 | self.pntr += 1 43 | def push(self, x): self.s.append(x) 44 | def pop(self): 45 | if len(self.s) == 0: self.pntr = len(self.code); return 0 46 | item = self.s.pop() 47 | if item is None: return 0 48 | return item 49 | #misc instructions 50 | def echo(self): print(self.pop()) 51 | def get_input(self): self.push(input('')) 52 | def get_time(self): self.push(time.time()) 53 | def info(self): print(str(self.pntr) + ': ' + str(self.s)) 54 | def size(self): self.push(len(self.s)) 55 | def file_call(self): self.pntr += 1; f = loader.fh.files[str(self.code[self.pntr])]; st = Stack(f[0], f[1], f[2]) 56 | def func_call(self): self.pntr += 1; st = Stack(self.functions[str(self.code[self.pntr])], self.strings, self.functions) 57 | def dup(self): x = self.pop(); self.push(x); self.push(x) 58 | def swap(self): x = self.pop(); y = self.pop(); self.push(x); self.push(y) 59 | def drop(self): self.pop() 60 | def get_func(self): self.pntr += 1; self.push(self.functions[self.code[self.pntr]]) 61 | def number(self): 62 | try: 63 | self.pntr += 1 64 | self.push(self.code[self.pntr]) 65 | except: 66 | self.push(0) 67 | def string(self): 68 | try: 69 | self.pntr += 1 70 | self.push(self.strings[int(self.code[self.pntr])]) 71 | except: 72 | self.push(0) 73 | print('error') 74 | def fetch(self): self.pntr += 1; self.push(self.mem[self.code[self.pntr]]) 75 | def store(self): self.pntr += 1; self.mem[self.code[self.pntr]] = self.pop() 76 | def run(self): p = parser.tokenize(lines); lines = [self.pop()]; st = Stack(p[0], p[1], p[2]) 77 | def src(self): self.push(" ".join(str(self.code))) 78 | #io instructions 79 | def write(self): 80 | path = os.path.join(os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))), 'scripts', str(self.pop()) + '.txt') 81 | with open(path, 'w') as out_file: 82 | out_file.write(str(self.pop())) 83 | def read(self): 84 | path = os.path.join(os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))), 'scripts', str(self.pop()) + '.txt') 85 | with open(path, 'r') as out_file: 86 | self.push(out_file.readlines()) 87 | #math instructions 88 | def increment(self): self.push(num(self.pop()) + 1) 89 | def decrement(self): self.push(num(self.pop()) - 1) 90 | def plus(self): self.push(num(self.pop()) + num(self.pop())) 91 | def minus(self): n = self.pop(); self.push(num(self.pop()) - num(n)) 92 | def star(self): self.push(num(self.pop()) * num(self.pop())) 93 | def slash(self): 94 | try: 95 | n = self.pop() 96 | self.push(num(self.pop()) // num(n)) 97 | except: 98 | self.push(0) 99 | def modulus(self): 100 | n = self.pop() 101 | try: 102 | n = num(self.pop()) % num(n) 103 | except: 104 | self.push(0) 105 | def power(self): 106 | try: 107 | n = self.pop() 108 | self.push(num(self.pop()) ** num(n)) 109 | except: 110 | self.push(0) 111 | 112 | def orOp(self): 113 | try: 114 | self.push(self.pop() | self.pop()) 115 | except: 116 | self.push(0) 117 | def negate(self): self.push(-num(self.pop())) 118 | def square_root(self): 119 | try: 120 | self.push(math.sqrt(num(self.pop()))) 121 | except: 122 | self.push(0) 123 | def cos(self): self.push(math.cos(num(self.pop()))) 124 | def sin(self): self.push(math.sin(num(self.pop()))) 125 | def tan(self): self.push(math.tan(num(self.pop()))) 126 | #string instructions 127 | def concat(self): n = self.pop(); self.push(str(self.pop()) + str(n)) 128 | def join(self): n = self.pop(); self.push(str(self.pop()) + ' ' + str(n)) 129 | def join_new_line(self): n = self.pop(); self.push(str(self.pop()) + '\n' + str(n)) 130 | def sub(self): n = self.pop(); m = self.pop(); self.push(re.sub(str(self.pop()), str(m), str(n))) 131 | def rnd_str(self): self.push(u''.join(random.choice(string.ascii_lowercase) for x in range(num(self.pop())))) 132 | def starts(self): n = self.pop(); self.push(str(self.pop()).startswith(str(n))) 133 | def ends(self): n = self.pop(); self.push(str(self.pop()).endswith(str(n))) 134 | #equality instructions 135 | def equals(self): self.push(self.pop() == self.pop()) 136 | def notequals(self): self.push(self.pop() != self.pop()) 137 | def less_than(self): self.push(self.pop()) > num(self.pop()) 138 | def greater_than(self): self.push(self.pop()) < num(self.pop()) 139 | def less_or_equal(self): self.push(self.pop()) >= num(self.pop()) 140 | def greater_or_equal(self): self.push(self.pop()) <= num(self.pop()) 141 | def eval_py(self): self.push(eval(self.pop())) 142 | def and_op(self): self.push(self.pop() and self.pop()) 143 | #loop instructions 144 | def for_op(self): 145 | cond = self.pop() 146 | end = self.pop() 147 | start = self.pop() 148 | loop_index = self.pop() 149 | self.mem[loop_index] = start 150 | while int(self.mem[loop_index]) < num(end): 151 | st = Stack(loader.substitute_tokens(cond), self.strings, self.functions) 152 | self.mem[loop_index] = num(self.mem[loop_index]) + 1 153 | def while_op(self): 154 | cond = self.pop() 155 | cond_address = self.pop() 156 | self.mem[cond_address] = True 157 | while self.mem[cond_address]: 158 | st = Stack(loader.substitute_tokens(cond), self.strings, self.functions) 159 | #branch instructions 160 | def if_else_branch(self): 161 | false_cond = self.pop() 162 | true_cond = self.pop() 163 | cond = self.pop() 164 | if cond: st = Stack(loader.substitute_tokens(true_cond), self.strings, self.functions) 165 | else: st = Stack(loader.substitute_tokens(false_cond), self.strings, self.functions) 166 | def if_branch(self): 167 | true_cond = self.pop() 168 | cond = self.pop() 169 | if cond: st = Stack(loader.substitute_tokens(true_cond), self.strings, self.functions) 170 | ############################################################################################################################################### 171 | inst = [file_call, while_op, for_op, if_branch, get_input, info, echo, dup, swap, drop, plus, minus, star, slash, orOp, negate, 172 | equals, notequals, fetch, store, less_than, greater_than, less_or_equal, greater_or_equal, modulus, power, concat, sub, 173 | square_root, cos, sin, tan, join, size, increment, decrement, write, join_new_line, rnd_str, starts, ends, get_time, 174 | read, if_else_branch, run, src, number, string, func_call, eval_py, get_func, and_op] 175 | ############################################################################################################################################### 176 | --------------------------------------------------------------------------------