Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__pycache__
8 changes: 7 additions & 1 deletion data.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@ def read_all(self):

def write(self, variable, expression):
variable_name = variable.value
self.variables[variable_name] = expression
self.variables[variable_name] = expression

def includes(self, id):
if id in self.variables:
return True

return False
50 changes: 50 additions & 0 deletions execute.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from lexer import Lexer
from parse import Parser
from interpreter import Interpreter
from data import Data
import argparse


class Executor:
def __init__(self):
self.data = Data()

def execute(self, path):
if not path.endswith('.shs'):
print('Error: Invalid file extension. Supported Extensions:\n -.shs')
return

try:
with open(path) as file:
lines = file.readlines()
for idx, line in enumerate(lines):
text = line.strip()
if text == '':
continue
elif text.strip() == 'exit':
break

tokenizer = Lexer(text)
tokens = tokenizer.tokenize()

parser = Parser(tokens, self.data)
tree = parser.parse()

interpreter = Interpreter(tree, self.data)
result = interpreter.interpret()

if result is not None:
print(f'{path}:{idx + 1}:', result)

except:
print(f'Error: File {path} not found.')


if __name__ == "__main__":
description = "executor program for ShadowScript"
parser = argparse.ArgumentParser(description=description)
parser.add_argument("path", help="file path")
args = parser.parse_args()

executor = Executor()
executor.execute(args.path)
88 changes: 40 additions & 48 deletions interpreter.py
Original file line number Diff line number Diff line change
@@ -1,127 +1,119 @@
from tokens import Integer, Float, Reserved


class Interpreter:
def __init__(self, tree, base):
self.tree = tree
self.data = base

def read_INT(self, value):
return int(value)

def read_FLT(self, value):
return float(value)

def read_VAR(self, id):
variable = self.data.read(id)
variable_type = variable.type

return getattr(self, f"read_{variable_type}")(variable.value)
return getattr(self, f'read_{variable_type}')(variable.value)

def compute_bin(self, left, op, right):
left_type = "VAR" if str(left.type).startswith("VAR") else str(left.type)
right_type = "VAR" if str(right.type).startswith("VAR") else str(right.type)
if op.value == "=":
left.type = f"VAR({right_type})"
left_type = 'VAR' if str(left.type).startswith(
'VAR') else str(left.type)
right_type = 'VAR' if str(right.type).startswith(
'VAR') else str(right.type)
if op.value == '=':
left.type = f'VAR({right_type})'
self.data.write(left, right)
return self.data.read_all()

left = getattr(self, f"read_{left_type}")(left.value)
right = getattr(self, f"read_{right_type}")(right.value)
left = getattr(self, f'read_{left_type}')(left.value)
right = getattr(self, f'read_{right_type}')(right.value)

if op.value == "+":
if op.value == '+':
output = left + right
elif op.value == "-":
elif op.value == '-':
output = left - right
elif op.value == "*":
elif op.value == '*':
output = left * right
elif op.value == "/":
elif op.value == '/':
output = left / right
elif op.value == ">":
elif op.value == '>':
output = 1 if left > right else 0
elif op.value == ">=":
elif op.value == '>=':
output = 1 if left >= right else 0
elif op.value == "<":
elif op.value == '<':
output = 1 if left < right else 0
elif op.value == "<=":
elif op.value == '<=':
output = 1 if left <= right else 0
elif op.value == "?=":
elif op.value == '?=':
output = 1 if left == right else 0
elif op.value == "and":
elif op.value == 'and':
output = 1 if left and right else 0
elif op.value == "or":
elif op.value == 'or':
output = 1 if left or right else 0

return Integer(output) if (left_type == "INT" and right_type == "INT") else Float(output)

return Integer(output) if (left_type == 'INT' and right_type == 'INT') else Float(output)

def compute_unary(self, operator, operand):
operand_type = "VAR" if str(operand.type).startswith("VAR") else str(operand.type)
operand_type = 'VAR' if str(operand.type).startswith(
'VAR') else str(operand.type)

operand = getattr(self, f"read_{operand_type}")(operand.value)
operand = getattr(self, f'read_{operand_type}')(operand.value)

if operator.value == "+":
if operator.value == '+':
output = +operand
elif operator.value == "-":
elif operator.value == '-':
output = -operand
elif operator.value == "not":
elif operator.value == 'not':
output = 1 if not operand else 0
return Integer(output) if (operand_type == "INT") else Float(output)

return Integer(output) if (operand_type == 'INT') else Float(output)

def interpret(self, tree=None):
if tree is None:
tree = self.tree

if isinstance(tree, list):
if isinstance(tree[0], Reserved):
if tree[0].value == "if":
if tree[0].value == 'if':
for idx, condition in enumerate(tree[1][0]):
evaluation = self.interpret(condition)
if evaluation.value == 1:
return self.interpret(tree[1][1][idx])

if len(tree[1]) == 3:
return self.interpret(tree[1][2])

else:
return
elif tree[0].value == "while":
elif tree[0].value == 'while':
condition = self.interpret(tree[1][0])

while condition.value == 1:
# Doing the action
print(self.interpret(tree[1][1]))

# Checking the condition
condition = self.interpret(tree[1][0])

return

# Unary operation
if isinstance(tree, list) and len(tree) == 2:
expression = tree[1]
if isinstance(expression, list):
expression = self.interpret(expression)
return self.compute_unary(tree[0], expression)

# No operation

elif not isinstance(tree, list):
return tree

else:
# Post order traversal

# Evaluating left subtree
else:
left_node = tree[0]
if isinstance(left_node, list):
left_node = self.interpret(left_node)

# Evaluating right subtree
right_node = tree[2]
if isinstance(right_node, list):
right_node = self.interpret(right_node)

# Evaluating root node
operator = tree[1]
return self.compute_bin(left_node, operator, right_node)

54 changes: 26 additions & 28 deletions lexer.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@
from tokens import Integer, Float, Operation, Declaration, Variable, Boolean, Comparison, Reserved
import string

# make varname = 50

class Lexer:
# while <expr> do <statement>
digits = "0123456789"
letters = "abcdefghijklmnopqrstuvwxyz"
operations = "+-/*()="
stopwords = [" "]
declarations = ["make"]
boolean = ["and", "or", "not"]
comparisons = [">", "<", ">=", "<=", "?="]
specialCharacters = "><=?"
reserved = ["if", "elif", "else", "do", "while"]
digits = string.digits
letters = string.ascii_lowercase
operations = '+-/*()='
stopwords = [' ']
declarations = ['make']
boolean = ['and', 'or', 'not']
comparisons = ['>', '<', '>=', '<=', '?=']
specialCharacters = '><=?'
reserved = ['if', 'elif', 'else', 'do', 'while']

def __init__(self, text):
self.text = text
self.idx = 0
self.tokens = []
self.char = self.text[self.idx]
self.token = None

def tokenize(self):
while self.idx < len(self.text):
if self.char in Lexer.digits:
self.token = self.extract_number()

elif self.char in Lexer.operations:
self.token = Operation(self.char)
self.move()

elif self.char in Lexer.stopwords:
self.move()
continue
Expand All @@ -45,40 +44,39 @@ def tokenize(self):
self.token = Reserved(word)
else:
self.token = Variable(word)

elif self.char in Lexer.specialCharacters:
comparisonOperator = ""
comparisonOperator = ''
while self.char in Lexer.specialCharacters and self.idx < len(self.text):
comparisonOperator += self.char
self.move()

self.token = Comparison(comparisonOperator)

self.tokens.append(self.token)

return self.tokens

def extract_number(self):
number = ""
number = ''
isFloat = False
while (self.char in Lexer.digits or self.char == ".") and (self.idx < len(self.text)):
if self.char == ".":
while (self.char in Lexer.digits or self.char == '.') and (self.idx < len(self.text)):
if self.char == '.':
isFloat = True
number += self.char
self.move()

return Integer(number) if not isFloat else Float(number)

def extract_word(self):
word = ""
word = ''
while self.char in Lexer.letters and self.idx < len(self.text):
word += self.char
self.move()

return word

def move(self):
self.idx += 1
if self.idx < len(self.text):
self.char = self.text[self.idx]

Loading