teletype-simulator 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- teletype_simulator-0.1.0/PKG-INFO +5 -0
- teletype_simulator-0.1.0/pyproject.toml +12 -0
- teletype_simulator-0.1.0/setup.cfg +4 -0
- teletype_simulator-0.1.0/teletype_simulator/__init__.py +0 -0
- teletype_simulator-0.1.0/teletype_simulator/computer.py +53 -0
- teletype_simulator-0.1.0/teletype_simulator/csv_reader.py +7 -0
- teletype_simulator-0.1.0/teletype_simulator/interpreter.py +200 -0
- teletype_simulator-0.1.0/teletype_simulator/main.py +12 -0
- teletype_simulator-0.1.0/teletype_simulator/teletype.py +58 -0
- teletype_simulator-0.1.0/teletype_simulator.egg-info/PKG-INFO +5 -0
- teletype_simulator-0.1.0/teletype_simulator.egg-info/SOURCES.txt +12 -0
- teletype_simulator-0.1.0/teletype_simulator.egg-info/dependency_links.txt +1 -0
- teletype_simulator-0.1.0/teletype_simulator.egg-info/entry_points.txt +2 -0
- teletype_simulator-0.1.0/teletype_simulator.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "teletype-simulator"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A simple teletype and BASIC interpreter simulator"
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
|
|
11
|
+
[project.scripts]
|
|
12
|
+
teletype-sim = "teletype_simulator.main:main"
|
|
File without changes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from teletype_simulator.teletype import Teletype
|
|
2
|
+
from teletype_simulator.interpreter import BASIC_Interpreter
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
class Computer():
|
|
6
|
+
def __init__(self):
|
|
7
|
+
self.teletype = Teletype()
|
|
8
|
+
"""Instance of the Teletype object in Computer"""
|
|
9
|
+
|
|
10
|
+
self.interpreter = BASIC_Interpreter(self.teletype)
|
|
11
|
+
"""Instance of the interpreter in Computer"""
|
|
12
|
+
|
|
13
|
+
self.script_name = None
|
|
14
|
+
"""Stores the name of BASIC scripts being ran/created"""
|
|
15
|
+
|
|
16
|
+
self.script = None
|
|
17
|
+
"""Stores the actual BASIC script once it's loaded by open_program. HAS .txt APPENDED TO IT AT THE END AT INPUT"""
|
|
18
|
+
|
|
19
|
+
def configure(self):
|
|
20
|
+
self.teletype.output_from_teletype("Configuring BASIC Interpreter...")
|
|
21
|
+
#just a rizzy little thing I guess
|
|
22
|
+
|
|
23
|
+
def get_input(self):
|
|
24
|
+
option = self.teletype.input_to_teletype(0)
|
|
25
|
+
#Option runs from 1 - 3, 1 being create new program and 2 being opening a program; 3 is exit and is dealt with in Teletype
|
|
26
|
+
#This determines which function of the computer we're going to use
|
|
27
|
+
|
|
28
|
+
self.script_name = self.teletype.input_to_teletype(option)
|
|
29
|
+
#This gets the name of the script being created/opened from the user
|
|
30
|
+
|
|
31
|
+
if option == 1: #creates a new program and exits
|
|
32
|
+
self.new_program()
|
|
33
|
+
self.teletype.output_from_teletype("The program will now exit; you can work on the new script.")
|
|
34
|
+
sys.exit(0)
|
|
35
|
+
|
|
36
|
+
elif option == 2: #parses the script into self.script to be interpreted
|
|
37
|
+
self.open_program()
|
|
38
|
+
self.interpreter.interpret(self.script)
|
|
39
|
+
|
|
40
|
+
def new_program(self):
|
|
41
|
+
"""Opens a new .txt file for the user to write their BASIC scripts in."""
|
|
42
|
+
with open(f"user_scripts/{self.script_name}", "w") as file:
|
|
43
|
+
file.write("New script!\n")
|
|
44
|
+
|
|
45
|
+
def open_program(self):
|
|
46
|
+
"""Opens an existing .txt file for the interpreter to execute."""
|
|
47
|
+
try:
|
|
48
|
+
with open(f"user_scripts/{self.script_name}") as file:
|
|
49
|
+
self.script = file.read()
|
|
50
|
+
except FileNotFoundError:
|
|
51
|
+
print("File not found")
|
|
52
|
+
sys.exit(1)
|
|
53
|
+
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
class BASIC_Interpreter():
|
|
4
|
+
def __init__(self, teletype):
|
|
5
|
+
self.teletype = teletype
|
|
6
|
+
"""Dependency injection to allow easier outputting"""
|
|
7
|
+
|
|
8
|
+
self.script = None
|
|
9
|
+
"""Stores the actual BASIC script passed into the interpreter; line_number, command, argument"""
|
|
10
|
+
|
|
11
|
+
self.interpreted_script = None
|
|
12
|
+
"""Stores a parsed version of the script separated into keywords; a list of dicts"""
|
|
13
|
+
|
|
14
|
+
self.script_variables = dict()
|
|
15
|
+
"""Stores the variables in the script: {"variable name": x, "value": y}"""
|
|
16
|
+
|
|
17
|
+
self.loop_data = dict()
|
|
18
|
+
"""Stores data for loops"""
|
|
19
|
+
|
|
20
|
+
def interpret(self, script):
|
|
21
|
+
"""Interprets lines, separating them into line number, command, and argument for running"""
|
|
22
|
+
self.script = script
|
|
23
|
+
self.interpreted_script = list()
|
|
24
|
+
|
|
25
|
+
for line in self.script.splitlines():
|
|
26
|
+
line = line.split(maxsplit=2)
|
|
27
|
+
if len(line) != 3:
|
|
28
|
+
argument = None
|
|
29
|
+
else:
|
|
30
|
+
argument = line[2].strip()
|
|
31
|
+
interpreted_line = {
|
|
32
|
+
"line_number": int(line[0]),
|
|
33
|
+
"command": line[1].upper(),
|
|
34
|
+
"argument": argument
|
|
35
|
+
}
|
|
36
|
+
self.interpreted_script.append(interpreted_line)
|
|
37
|
+
|
|
38
|
+
self.run_program()
|
|
39
|
+
|
|
40
|
+
def run_program(self):
|
|
41
|
+
"""Continously runs the program and keeps track of the line index"""
|
|
42
|
+
index = 0
|
|
43
|
+
while index < len(self.interpreted_script):
|
|
44
|
+
#Continously runs the program
|
|
45
|
+
current_line = self.interpreted_script[index]
|
|
46
|
+
command = current_line["command"]
|
|
47
|
+
argument = current_line["argument"]
|
|
48
|
+
|
|
49
|
+
new_index = self.execute(index, command, argument)
|
|
50
|
+
if new_index is not None:
|
|
51
|
+
index = new_index
|
|
52
|
+
else:
|
|
53
|
+
index += 1
|
|
54
|
+
|
|
55
|
+
def execute(self, index, command, argument):
|
|
56
|
+
"""Executes each line of the program, matching commands to functions"""
|
|
57
|
+
if command == "PRINT":
|
|
58
|
+
self.print_method(argument)
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
elif command == "LET":
|
|
62
|
+
self.let_method(argument)
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
elif command == "GOTO":
|
|
66
|
+
return self.goto_method(argument)
|
|
67
|
+
|
|
68
|
+
elif command == "IF":
|
|
69
|
+
action = self.booleans(argument)
|
|
70
|
+
if action:
|
|
71
|
+
return self.execute(index, *(action.split(maxsplit=1)))
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
elif command == "INPUT":
|
|
75
|
+
self.input_method(argument)
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
elif command == "FOR":
|
|
79
|
+
self.for_loop(index, argument)
|
|
80
|
+
|
|
81
|
+
elif command == "NEXT":
|
|
82
|
+
new_index = self.next_method(argument)
|
|
83
|
+
if new_index is not None:
|
|
84
|
+
return new_index
|
|
85
|
+
|
|
86
|
+
elif command == "END":
|
|
87
|
+
sys.exit(0)
|
|
88
|
+
|
|
89
|
+
else:
|
|
90
|
+
print("FATAL ERROR")
|
|
91
|
+
sys.exit(1)
|
|
92
|
+
|
|
93
|
+
def print_method(self, argument):
|
|
94
|
+
"""prints variables"""
|
|
95
|
+
printed_thing = None
|
|
96
|
+
if argument in self.script_variables:
|
|
97
|
+
printed_thing = self.script_variables[argument]
|
|
98
|
+
else:
|
|
99
|
+
printed_thing = argument
|
|
100
|
+
|
|
101
|
+
self.teletype.output_from_teletype(str(printed_thing).strip('"'))
|
|
102
|
+
|
|
103
|
+
def let_method(self, argument):
|
|
104
|
+
"""assigns a value to data"""
|
|
105
|
+
argument = argument.split("=")
|
|
106
|
+
argument[0] = argument[0].strip()
|
|
107
|
+
argument[1] = argument[1].strip()
|
|
108
|
+
|
|
109
|
+
for operator in ["+", "-", "*", "/"]:
|
|
110
|
+
if operator in argument[1]:
|
|
111
|
+
argument[1] = self.evaluate_expression(argument[1])
|
|
112
|
+
break
|
|
113
|
+
|
|
114
|
+
self.script_variables[argument[0]] = argument[1]
|
|
115
|
+
|
|
116
|
+
def goto_method(self, line_number):
|
|
117
|
+
"""goes from one line to another line, skipping lines in between"""
|
|
118
|
+
for index, line in enumerate(self.interpreted_script):
|
|
119
|
+
if line["line_number"] == int(line_number):
|
|
120
|
+
return index
|
|
121
|
+
|
|
122
|
+
def evaluate_expression(self, expression):
|
|
123
|
+
"""Does basic mathematical calculations; Compares two numbers with operators"""
|
|
124
|
+
expression = expression.split()
|
|
125
|
+
|
|
126
|
+
left = expression[0].strip()
|
|
127
|
+
right = expression[2].strip()
|
|
128
|
+
if not left.isnumeric():
|
|
129
|
+
left = int(self.script_variables[left])
|
|
130
|
+
else:
|
|
131
|
+
left = int(left)
|
|
132
|
+
if not right.isnumeric():
|
|
133
|
+
right = int(self.script_variables[right])
|
|
134
|
+
else:
|
|
135
|
+
right = int(right)
|
|
136
|
+
|
|
137
|
+
operator = expression[1].strip()
|
|
138
|
+
if operator == "+":
|
|
139
|
+
return left + right
|
|
140
|
+
elif operator == "-":
|
|
141
|
+
return left - right
|
|
142
|
+
elif operator == "*":
|
|
143
|
+
return left * right
|
|
144
|
+
elif operator == "/":
|
|
145
|
+
return left / right
|
|
146
|
+
elif operator == ">":
|
|
147
|
+
return True if left > right else False
|
|
148
|
+
elif operator == ">=":
|
|
149
|
+
return True if left >= right else False
|
|
150
|
+
elif operator == "==":
|
|
151
|
+
return True if left == right else False
|
|
152
|
+
elif operator == "<":
|
|
153
|
+
return True if left < right else False
|
|
154
|
+
elif operator == "<=":
|
|
155
|
+
return True if left <= right else False
|
|
156
|
+
|
|
157
|
+
def input_method(self, argument):
|
|
158
|
+
"""asks for an input and stores it in a variable"""
|
|
159
|
+
prompt, variable = argument.split(";")
|
|
160
|
+
prompt = prompt.strip()
|
|
161
|
+
variable = variable.strip()
|
|
162
|
+
|
|
163
|
+
self.print_method(prompt)
|
|
164
|
+
user_input = self.teletype.input_to_teletype(3)
|
|
165
|
+
|
|
166
|
+
self.let_method(f"{variable} = {user_input}")
|
|
167
|
+
|
|
168
|
+
def booleans(self, expression):
|
|
169
|
+
"""Evaluates boolean expressions"""
|
|
170
|
+
condition, action = expression.split("THEN")
|
|
171
|
+
condition = condition.strip()
|
|
172
|
+
action = action.strip()
|
|
173
|
+
|
|
174
|
+
if self.evaluate_expression(condition):
|
|
175
|
+
return action
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
def for_loop(self, index, argument):
|
|
179
|
+
"""For loop"""
|
|
180
|
+
variable, bound = argument.split("=")
|
|
181
|
+
start, end = bound.split("TO")
|
|
182
|
+
variable = variable.strip()
|
|
183
|
+
start = int(start.strip())
|
|
184
|
+
end = int(end.strip())
|
|
185
|
+
|
|
186
|
+
self.script_variables[variable] = start
|
|
187
|
+
self.loop_data[variable] = {"end": end, "start_index": index + 1}
|
|
188
|
+
|
|
189
|
+
def next_method(self, loop_variable_name):
|
|
190
|
+
"""Advances a for loop and see if it should continue"""
|
|
191
|
+
loop_variable_name = loop_variable_name.strip()
|
|
192
|
+
self.script_variables[loop_variable_name] += 1
|
|
193
|
+
|
|
194
|
+
loop_variable_value = self.script_variables[loop_variable_name]
|
|
195
|
+
loop_data = self.loop_data[loop_variable_name]
|
|
196
|
+
|
|
197
|
+
if loop_variable_value <= loop_data["end"]:
|
|
198
|
+
return loop_data["start_index"]
|
|
199
|
+
|
|
200
|
+
return None
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import sys, time
|
|
2
|
+
|
|
3
|
+
OUTPUT_SPEED = 0
|
|
4
|
+
"""A teletype returns about 10 characters per second according to research; the output speed is also hardwired to this in this program."""
|
|
5
|
+
|
|
6
|
+
class Teletype():
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.script_name = None
|
|
9
|
+
|
|
10
|
+
def input_to_teletype(self, log_in: int):
|
|
11
|
+
if log_in == 0:
|
|
12
|
+
try:
|
|
13
|
+
self.output_from_teletype("Enter 1 to create a new script, 2 to run an existing one, 3 to exit, \"commands\" for command menu: ")
|
|
14
|
+
option = input("\nINPUT > ")
|
|
15
|
+
if option not in ["1", "2", "3", "commands"]:
|
|
16
|
+
raise ValueError
|
|
17
|
+
|
|
18
|
+
if option == "commands":
|
|
19
|
+
self.commands()
|
|
20
|
+
print()
|
|
21
|
+
sys.exit(0)
|
|
22
|
+
option = int(option)
|
|
23
|
+
|
|
24
|
+
except ValueError,:
|
|
25
|
+
#Exits if none of the three numeric options are picked
|
|
26
|
+
print("Invalid input")
|
|
27
|
+
sys.exit(1)
|
|
28
|
+
if option == 3:
|
|
29
|
+
sys.exit(0)
|
|
30
|
+
return option
|
|
31
|
+
|
|
32
|
+
elif log_in == 1 or log_in == 2: #called by Computer to get a script name
|
|
33
|
+
if log_in == 1:
|
|
34
|
+
self.output_from_teletype("New script name: ") #For new scripts
|
|
35
|
+
else:
|
|
36
|
+
self.output_from_teletype("Name of the script you want to run: ") #For existing scripts
|
|
37
|
+
|
|
38
|
+
self.script_name = input("\nINPUT > ")
|
|
39
|
+
self.script_name += ".txt" #.txt extension is ADDED TO THE FILE NAME HERE
|
|
40
|
+
return self.script_name
|
|
41
|
+
|
|
42
|
+
elif log_in == 3: #Called by INPUT in interpreter to get an input from the user
|
|
43
|
+
return input("\nINPUT > ")
|
|
44
|
+
|
|
45
|
+
def output_from_teletype(self, message: str):
|
|
46
|
+
print("\nOUTPUT > ", end="")
|
|
47
|
+
for character in message:
|
|
48
|
+
print(character, end="", flush=True)
|
|
49
|
+
time.sleep(OUTPUT_SPEED)
|
|
50
|
+
#it outputs character by characteron a line
|
|
51
|
+
|
|
52
|
+
def commands(self):
|
|
53
|
+
"""Displays command menu for help"""
|
|
54
|
+
with open("commands.txt", "r") as file:
|
|
55
|
+
commands = file.read()
|
|
56
|
+
|
|
57
|
+
for line in commands.splitlines():
|
|
58
|
+
self.output_from_teletype(line)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
pyproject.toml
|
|
2
|
+
teletype_simulator/__init__.py
|
|
3
|
+
teletype_simulator/computer.py
|
|
4
|
+
teletype_simulator/csv_reader.py
|
|
5
|
+
teletype_simulator/interpreter.py
|
|
6
|
+
teletype_simulator/main.py
|
|
7
|
+
teletype_simulator/teletype.py
|
|
8
|
+
teletype_simulator.egg-info/PKG-INFO
|
|
9
|
+
teletype_simulator.egg-info/SOURCES.txt
|
|
10
|
+
teletype_simulator.egg-info/dependency_links.txt
|
|
11
|
+
teletype_simulator.egg-info/entry_points.txt
|
|
12
|
+
teletype_simulator.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
teletype_simulator
|