├── .gitignore ├── .gitattributes ├── program1.oll ├── program2.oll ├── readme.md ├── program3.oll ├── LICENSE ├── interpreter.py └── main.py /.gitignore: -------------------------------------------------------------------------------- 1 | video/ -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /program1.oll: -------------------------------------------------------------------------------- 1 | READ 2 | READ 3 | SUB 4 | JUMP.EQ.0 L1 5 | PRINT "not equal" 6 | HALT 7 | 8 | L1: 9 | PRINT "equal" 10 | HALT -------------------------------------------------------------------------------- /program2.oll: -------------------------------------------------------------------------------- 1 | PUSH 232 2 | PUSH 1 3 | ADD 4 | JUMP.EQ.0 L1 5 | 6 | LOOP: 7 | PUSH 2 8 | SUB 9 | JUMP.EQ.0 L1 10 | JUMP.GT.0 LOOP 11 | PRINT "even" 12 | HALT 13 | 14 | L1: 15 | PRINT "odd" 16 | HALT -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ### Will maybe add some info later. 2 | But for now watch the video below. 3 | 4 | ### What is this? 5 | Watch the explanation here: [youtube-video-explanation](https://www.youtube.com/watch?v=A3gTw1ZkeK0) -------------------------------------------------------------------------------- /program3.oll: -------------------------------------------------------------------------------- 1 | PUSH 69 2 | JUMP.EQ.0 L2 3 | JUMP.GT.0 L0 4 | PRINT "A" 5 | HALT 6 | 7 | L0: 8 | PUSH 3 9 | SUB 10 | JUMP.EQ.0 L1 11 | JUMP.GT.0 L0 12 | PRINT "B" 13 | HALT 14 | 15 | L1: 16 | PRINT "C" 17 | HALT 18 | 19 | L2: 20 | PRINT "D" 21 | HALT -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 basvdl97 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 | -------------------------------------------------------------------------------- /interpreter.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | # read arguments 4 | program_filepath = sys.argv[1] 5 | 6 | 7 | ########################### 8 | # Tokenize Program 9 | ########################### 10 | 11 | # read file lines 12 | program_lines = [] 13 | with open(program_filepath, "r") as program_file: 14 | program_lines = [ 15 | line.strip() 16 | for line in program_file.readlines()] 17 | 18 | program = [] 19 | token_counter = 0 20 | label_tracker = {} 21 | for line in program_lines: 22 | parts = line.split(" ") 23 | opcode = parts[0] 24 | 25 | # check for empty line 26 | if opcode == "": 27 | continue 28 | 29 | # check if its a label 30 | if opcode.endswith(":"): 31 | label_tracker[opcode[:-1]] = token_counter 32 | continue 33 | 34 | # store opcode token 35 | program.append(opcode) 36 | token_counter += 1 37 | 38 | # handle each opcode 39 | if opcode == "PUSH": 40 | # expecting a number 41 | number = int(parts[1]) 42 | program.append(number) 43 | token_counter += 1 44 | elif opcode == "PRINT": 45 | # parse string literal 46 | string_literal = ' '.join(parts[1:])[1:-1] 47 | program.append(string_literal) 48 | token_counter += 1 49 | elif opcode == "JUMP.EQ.0": 50 | # read label 51 | label = parts[1] 52 | program.append(label) 53 | token_counter += 1 54 | elif opcode == "JUMP.GT.0": 55 | # read label 56 | label = parts[1] 57 | program.append(label) 58 | token_counter += 1 59 | 60 | ########################### 61 | # Interpret Program 62 | ########################### 63 | 64 | class Stack: 65 | 66 | def __init__(self, size): 67 | self.buf = [0 for _ in range(size)] 68 | self.sp = -1 69 | 70 | def push(self, number): 71 | self.sp += 1 72 | self.buf[self.sp] = number 73 | 74 | def pop(self): 75 | number = self.buf[self.sp] 76 | self.sp -= 1 77 | return number 78 | 79 | def top(self): 80 | return self.buf[self.sp] 81 | 82 | 83 | 84 | pc = 0 85 | stack = Stack(256) 86 | 87 | while program[pc] != "HALT": 88 | opcode = program[pc] 89 | pc += 1 90 | 91 | if opcode == "PUSH": 92 | number = program[pc] 93 | pc += 1 94 | 95 | stack.push(number) 96 | elif opcode == "POP": 97 | stack.pop() 98 | elif opcode == "ADD": 99 | a = stack.pop() 100 | b = stack.pop() 101 | stack.push(a+b) 102 | elif opcode == "SUB": 103 | a = stack.pop() 104 | b = stack.pop() 105 | stack.push(b-a) 106 | elif opcode == "PRINT": 107 | string_literal = program[pc] 108 | pc += 1 109 | print(string_literal) 110 | elif opcode == "READ": 111 | number = int(input()) 112 | stack.push(number) 113 | elif opcode == "JUMP.EQ.0": 114 | number = stack.top() 115 | if number == 0: 116 | pc = label_tracker[program[pc]] 117 | else: 118 | pc += 1 119 | elif opcode == "JUMP.GT.0": 120 | number = stack.top() 121 | if number > 0: 122 | pc = label_tracker[program[pc]] 123 | else: 124 | pc += 1 -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | # read arguments 4 | program_filepath = sys.argv[1] 5 | 6 | 7 | ########################### 8 | # Tokenize Program 9 | ########################### 10 | 11 | # read file lines 12 | program_lines = [] 13 | with open(program_filepath, "r") as program_file: 14 | program_lines = [line.strip() for line in program_file.readlines()] 15 | 16 | program = [] 17 | token_counter = 0 18 | label_tracker = {} 19 | for line in program_lines: 20 | parts = line.split(" ") 21 | opcode = parts[0] 22 | 23 | # check for empty line 24 | if opcode == "": 25 | continue 26 | 27 | # check if its a label 28 | if opcode.endswith(":"): 29 | label_tracker[opcode[:-1]] = token_counter 30 | continue 31 | 32 | # store opcode token 33 | program.append(opcode) 34 | token_counter += 1 35 | 36 | # handle each opcode 37 | if opcode == "PUSH": 38 | # expecting a number 39 | number = int(parts[1]) 40 | program.append(number) 41 | token_counter += 1 42 | elif opcode == "PRINT": 43 | # parse string literal 44 | string_literal = ' '.join(parts[1:])[1:-1] 45 | program.append(string_literal) 46 | token_counter += 1 47 | elif opcode == "JUMP.EQ.0": 48 | # read label 49 | label = parts[1] 50 | program.append(label) 51 | token_counter += 1 52 | elif opcode == "JUMP.GT.0": 53 | # read label 54 | label = parts[1] 55 | program.append(label) 56 | token_counter += 1 57 | 58 | ########################### 59 | # Create Stack 60 | ########################### 61 | 62 | class Stack: 63 | 64 | def __init__(self, size): 65 | self.buf = [0 for _ in range(size)] 66 | self.sp = -1 67 | 68 | def push(self, number): 69 | self.sp += 1 70 | self.buf[self.sp] = number 71 | 72 | def pop(self): 73 | number = self.buf[self.sp] 74 | self.sp -= 1 75 | return number 76 | 77 | def top(self): 78 | return self.buf[self.sp] 79 | 80 | ########################### 81 | # Interpret Program 82 | ########################### 83 | 84 | pc = 0 85 | stack = Stack(256) 86 | 87 | while program[pc] != "HALT": 88 | opcode = program[pc] 89 | pc += 1 90 | 91 | if opcode == "PUSH": 92 | number = program[pc] 93 | pc += 1 94 | 95 | stack.push(number) 96 | elif opcode == "POP": 97 | stack.pop() 98 | elif opcode == "ADD": 99 | a = stack.pop() 100 | b = stack.pop() 101 | stack.push(a+b) 102 | elif opcode == "SUB": 103 | a = stack.pop() 104 | b = stack.pop() 105 | stack.push(b-a) 106 | elif opcode == "PRINT": 107 | string_literal = program[pc] 108 | pc += 1 109 | print(string_literal) 110 | elif opcode == "READ": 111 | number = int(input()) 112 | stack.push(number) 113 | elif opcode == "JUMP.EQ.0": 114 | number = stack.top() 115 | if number == 0: 116 | pc = label_tracker[program[pc]] 117 | else: 118 | pc += 1 119 | elif opcode == "JUMP.GT.0": 120 | number = stack.top() 121 | if number > 0: 122 | pc = label_tracker[program[pc]] 123 | else: 124 | pc += 1 125 | else: 126 | print("Unexpected opcode received") 127 | exit(1) --------------------------------------------------------------------------------