Skip to content

Commit

Permalink
Merge pull request #2 from mahdidehghandev/evaluate_update
Browse files Browse the repository at this point in the history
Evaluate update
  • Loading branch information
EhsanShafi3i authored Dec 9, 2024
2 parents 574a92b + 116e11c commit af7bc7a
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 113 deletions.
66 changes: 36 additions & 30 deletions evaluate.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,38 @@
import math


class Evaluate:

def __init__(self, postfix_expr):
def __init__(self, postfix_expr,symbol_table):
self.postfix_expr = postfix_expr
self.identifier_vals = {}
self.symbol_table = symbol_table

@staticmethod
def _reserved_ids():
return ['div', 'mod', 'sin', 'cos', 'tan', 'cot', 'arcsin', 'arccos',
'arctan', 'arccot', 'log', 'sqrt', 'sqr', 'exp']
# @staticmethod
# def _reserved_ids():
# return ['div', 'mod', 'sin', 'cos', 'tan', 'cot', 'arcsin', 'arccos',
# 'arctan', 'arccot', 'log', 'sqrt', 'sqr', 'exp']


@staticmethod
def convert_to_num(entry):
try:
if entry in ["e","E"]:#! add another method
return 2.71
num = float(entry)
return int(num) if num.is_integer() else num
except ValueError:
raise Exception("ERROR: Invalid number format")

@staticmethod
def is_single_op(element):
ops = ['sin', 'cos', 'tan', 'cot', 'arcsin', 'arccos',
'arctan', 'arccot', 'log', 'sqrt', 'sqr', 'exp', 'unary-']
return element in ops
# @staticmethod
# def is_single_op(element):
# ops = ['sin', 'cos', 'tan', 'cot', 'arcsin', 'arccos',
# 'arctan', 'arccot', 'log', 'sqrt', 'sqr', 'exp', 'unary-']
# return element in ops

@staticmethod
def is_binary_op(element):
ops = ['+', '-', 'mod', 'div', '*', '/', '^']
return element in ops
# @staticmethod
# def is_binary_op(element):
# ops = ['+', '-', 'mod', 'div', '*', '/', '^']
# return element in ops


@staticmethod
Expand All @@ -47,6 +49,9 @@ def _apply_binary_op(op, a, b):
elif op == '^':
return a ** b
elif op == 'mod':
if isinstance(b, float): #! check if there is a float number after the mod
raise Exception("Syntax error: 'mod' requires an integer operand")

return a % b
elif op == 'div':
return a // b
Expand Down Expand Up @@ -97,31 +102,32 @@ def _apply_single_op(op, a):

def get_values(self):
for token in self.postfix_expr:
if token.type == "ID" and token.value not in self._reserved_ids():
if token.type == "ID" and not self.symbol_table.is_reserved(token.value.lower()):
token_lower = token.value.lower()

if token_lower not in self.identifier_vals and token_lower != 'e':
if token_lower not in self.symbol_table.table and token_lower != 'e':
value = input(f"Enter the value for '{token.value}': ")
result = self.convert_to_num(value)
self.identifier_vals[token_lower] = result
self.symbol_table.table[token_lower] = {"type" : "IDENTIFIER" , "value" :result , "is_reserved" : None}
elif token_lower == 'e':
value = 2.71
result = self.convert_to_num(value)
self.identifier_vals[token_lower] = result

self.symbol_table.table[token_lower] = {"type" : "IDENTIFIER" , "value" :result, "is_reserved" : None}#! use another method

def put_values(self, value):
for token in self.postfix_expr:
if token.type == "ID" and token.value not in self._reserved_ids():
if token.type == "ID" and not self.symbol_table.is_reserved(token.value):#!why don't use token.value.lower()
token_lower = token.value.lower()

if token_lower not in self.identifier_vals and token_lower != 'e':
if token_lower not in self.symbol_table.table and token_lower != 'e':
value = input(f"Enter the value for '{token.value}': ")
result = self.convert_to_num(value)
self.identifier_vals[token_lower] = result
self.symbol_table.table[token_lower] = {"type" : "IDENTIFIER" , "value" :result, "is_reserved" : None}
elif token_lower == 'e':
value = 2.71
result = self.convert_to_num(value)
self.identifier_vals[token_lower] = result
self.symbol_table.table[token_lower] = {"type" : "IDENTIFIER" , "value" :result, "is_reserved" : None}#! use another method


def is_number(self, entry):
Expand All @@ -140,20 +146,20 @@ def evaluate_expression(self):
operand_stack = []

for element in self.postfix_expr:
if element.type == "ID" and element.value not in self._reserved_ids():
operand_stack.append(self.identifier_vals[element.value.lower()])
elif self.is_binary_op(element.value):
if element.type == "ID" and not self.symbol_table.is_reserved(element.value.lower()):
operand_stack.append(self.symbol_table.table[element.value.lower()]["value"])
elif self.symbol_table.is_binary_op(element.value.lower()):
if len(operand_stack) < 2:
raise Exception("Insufficient operands for binary operation.")
top_element = operand_stack.pop()
bottom_element = operand_stack.pop()
operand_stack.append(self._apply_binary_op(element.value, bottom_element, top_element))
operand_stack.append(self._apply_binary_op(element.value.lower(), bottom_element, top_element))

elif self.is_single_op(element.value):
elif self.symbol_table.is_single_op(element.value.lower()):
if len(operand_stack) < 1:
raise Exception("Insufficient operands for binary operation.")
top_element = operand_stack.pop()
operand_stack.append(self._apply_single_op(element.value, top_element))
operand_stack.append(self._apply_single_op(element.value.lower(), top_element))

elif self.is_number(element.value):
operand_stack.append(self.convert_to_num(element.value))
Expand Down
103 changes: 82 additions & 21 deletions lexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ def __repr__(self):


class Lexer:
def __init__(self, text):
def __init__(self, symbol_table,text):
self.symbol_table = symbol_table
self.text = text
self.line = 1
self.beginning = 0
self.forward = 0
self.operators = ['+', '-', '*', '/', 'div', 'mod', '^', '(', ')']
# ! delete
# self.operators = ['+', '-', '*', '/', 'div', 'mod', '^', '(', ')']
self.tokens = []


Expand All @@ -40,7 +42,8 @@ def tokenize(self):
self.is_single_line_comment()
elif char == '{':
self.is_multi_line_comment()
elif char in self.operators:
# elif char in self.operators:
elif self.symbol_table.is_operator(char):
self.is_operator()
elif char.isdigit():
self.is_number()
Expand Down Expand Up @@ -83,7 +86,7 @@ def skip_white_space(self):
if self.text[self.forward] == '\n':
self.line += 1
self.forward += 1
self.beginning = self.forward
self.beginning = self.forward


def is_number(self):
Expand Down Expand Up @@ -176,35 +179,93 @@ def is_number(self):
def is_identifier(self):
state = 0
while self.forward < len(self.text):
char = self.text[self.forward]
char = self.text[self.forward].lower()
match state:
case 0:
if char.isalpha() or char == '_':
state = 1
self.forward += 1
case 1:
if char.isalpha() or char == '_' or char.isdigit():
if char.isalnum() or char == '_':
state = 1
self.forward += 1
else:
state = 2

case 2:
self.create_token("ID")
self.beginning = self.forward
return

lexeme = self.text[self.beginning:self.forward].lower()
if self.symbol_table.is_not_reserved(lexeme) and not self.symbol_table.is_id_existence(lexeme):
self.symbol_table.add_id(lexeme)
if self.symbol_table.is_function(lexeme):

# if lexeme in ['sin', 'cos', 'tan', 'cot', 'arcsin', 'arccos', 'arctan', 'arccot', 'log', 'sqrt', 'sqr', 'exp']:
self.create_token("FUNCTION")
self.beginning = self.forward
return
else:
self.create_token("ID")
self.beginning = self.forward
return
if state == 1:
lexeme = self.text[self.beginning:self.forward].lower()
if not self.symbol_table.is_id_existence(lexeme):
self.symbol_table.add_id(lexeme)
if self.symbol_table.is_function(lexeme):
# if lexeme in ['sin', 'cos', 'tan', 'cot', 'arcsin', 'arccos', 'arctan', 'arccot', 'log', 'sqrt', 'sqr', 'exp']:
self.create_token("FUNCTION")
self.beginning = self.forward
else:
self.create_token("ID")
self.beginning = self.forward

else:
self.error()



def is_operator(self):
for op in self.operators:
if self.text[self.forward:self.forward + len(op)] == op:
self.forward += len(op)
self.create_token("OPERATOR")
self.beginning = self.forward
return

for op in self.symbol_table.table:
if self.text[self.forward:self.forward + len(op)].lower() == op:
symbol_info = self.symbol_table.table[op]
if symbol_info['type'] == 'OPERATOR' or symbol_info['type'] == 'DELIMITER':
self.forward += len(op)
self.create_token(symbol_info['type'])
self.beginning = self.forward
return
self.error()






# def is_identifier(self):
# state = 0
# while self.forward < len(self.text):
# char = self.text[self.forward]
# match state:
# case 0:
# if char.isalpha() or char == '_':
# state = 1
# self.forward += 1
# case 1:
# if char.isalpha() or char == '_' or char.isdigit():
# state = 1
# self.forward += 1
# else:
# state = 2

# case 2:
# self.create_token("ID")
# self.beginning = self.forward
# return
# if state in {1}:
# self.create_token("ID")
# else:
# self.error()


# def is_operator(self):
# for op in self.operators:
# if self.text[self.forward:self.forward + len(op)] == op:
# self.forward += len(op)
# self.create_token("OPERATOR")
# self.beginning = self.forward
# return
# self.error()
27 changes: 16 additions & 11 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import matplotlib.pyplot as plt
import numpy as np

from symbol_table import SymbolTable


def read_file(entry):
try:
Expand All @@ -16,12 +18,12 @@ def read_file(entry):
print(f"An unexpected error occurred: {e}")
return None

def find_root(start_of_the_interval, end_of_the_interval, accuracy, postfix_expr):
def find_root(start_of_the_interval, end_of_the_interval, accuracy, postfix_expr,symbol_table):
max_iterations = 1000

evaluate = Evaluate(postfix_expr)
evaluate = Evaluate(postfix_expr,symbol_table)
y1 = evaluate.evaluate_with_value(start_of_the_interval)
evaluate = Evaluate(postfix_expr)
evaluate = Evaluate(postfix_expr,symbol_table)
y2 = evaluate.evaluate_with_value(end_of_the_interval)

if y1 * y2 > 0:
Expand All @@ -30,7 +32,7 @@ def find_root(start_of_the_interval, end_of_the_interval, accuracy, postfix_expr
iteration = 0
while abs(end_of_the_interval - start_of_the_interval) > accuracy and iteration < max_iterations:
mid_point = (start_of_the_interval + end_of_the_interval) / 2
evaluate = Evaluate(postfix_expr)
evaluate = Evaluate(postfix_expr,symbol_table)
y_mid = evaluate.evaluate_with_value(mid_point)

if abs(y_mid) < accuracy:
Expand All @@ -52,12 +54,12 @@ def find_root(start_of_the_interval, end_of_the_interval, accuracy, postfix_expr
print(f"Root approximated at x = {(start_of_the_interval + end_of_the_interval) / 2}")


def visulaize(start_of_the_interval, end_of_the_interval, postfix_expr):
step = 0.2
def visulaize(start_of_the_interval, end_of_the_interval, postfix_expr,symbol_table):
step = 0.1
points = []

for i in np.arange(start_of_the_interval, end_of_the_interval, step):
evaluate = Evaluate(postfix_expr)
evaluate = Evaluate(postfix_expr,symbol_table)
y = evaluate.evaluate_with_value(i)
points.append((i, y))

Expand All @@ -80,17 +82,20 @@ def main(file_name):
text = read_file(file_name)
if not text:
return

lexer = Lexer(text)
symbol_table = SymbolTable()

lexer = Lexer(symbol_table, text)
tokens = lexer.tokenize()
parser = Parser(tokens)
print(tokens)
parser = Parser(symbol_table, tokens)
parser.parse()

print("Parsed postfix expression:")
for i in parser.postfix:
print(i.value, end=" ")
print("\n")


while True:
print("1- without visual")
print("2- with visual")
Expand All @@ -99,7 +104,7 @@ def main(file_name):
choice = input("--> ")

if choice == "1":
evaluate = Evaluate(parser.postfix)
evaluate = Evaluate(parser.postfix,symbol_table)
print(evaluate.evaluate())

elif choice == "2":
Expand Down
Loading

0 comments on commit af7bc7a

Please sign in to comment.