Skip to content

Commit ad493ee

Browse files
committed
We can now do excel cell references (only in if statements for now)
1 parent 4bb7424 commit ad493ee

6 files changed

+56
-4
lines changed

code_generator.py

+3
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,8 @@ def generate_AssignmentNode(self, node):
9494
def generate_IdentifierNode(self, node):
9595
return node.value
9696

97+
def generate_CellReferenceNode(self, node):
98+
return f"sheet['{node.value}'].value"
99+
97100

98101

custom_parser.py

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import logging
22
from dictionary import Dictionary
3-
from parser_nodes import AssignmentNode, ExpressionNode, NumberNode, IfNode
3+
from parser_nodes import AssignmentNode, ExpressionNode, NumberNode, IfNode, CellReferenceNode
44

55
logger = logging.getLogger(__name__)
66

@@ -71,6 +71,8 @@ def parse_primary(self, tokens):
7171
token = tokens[0]
7272
if token.type == Dictionary.INTEGER:
7373
return NumberNode(token.value), 1
74+
elif token.type == Dictionary.CELL:
75+
return self.parse_cell_reference(token), 1
7476
elif token.type == Dictionary.LEFT_PARENTHESES:
7577
# Find matching parenthesis to handle nested cases
7678
count = 1
@@ -150,6 +152,28 @@ def parse_assignment(self, line):
150152
# otherwise, parse the expression
151153
expression = self.parse_expression(variable_value)
152154
return AssignmentNode(variable, expression)
155+
156+
def parse_cell_reference(self, token):
157+
"""Parse a cell reference."""
158+
# token.value should be any amount of alphabetic characters followed by any amount of numeric characters
159+
# if there are no numeric characters, raise an error
160+
# if there are no alphabetic characters, raise an error
161+
# if there are any other characters, raise an error
162+
if not token.value:
163+
raise ValueError("Empty cell reference")
164+
if not any(char in token.value for char in Dictionary.NUMERIC_CHARACTERS):
165+
raise ValueError("Invalid cell reference")
166+
if not any(char in token.value for char in Dictionary.ALPHABETIC_CHARACTERS):
167+
raise ValueError("Invalid cell reference")
168+
if any(char not in Dictionary.NUMERIC_CHARACTERS + Dictionary.ALPHABETIC_CHARACTERS for char in token.value):
169+
raise ValueError("Invalid cell reference")
170+
# there can be no numeric characters before the alphabetic characters. SO go through the string until we find a numeric character, and then check if the rest of the string is numeric
171+
i = 0
172+
while i < len(token.value) and token.value[i] not in Dictionary.NUMERIC_CHARACTERS:
173+
i += 1
174+
if not token.value[i:].isnumeric():
175+
raise ValueError("Invalid cell reference")
176+
return CellReferenceNode(token.value)
153177

154178

155179

dictionary.py

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class Dictionary:
88
# TOKEN TYPES
99
INTEGER = 'INT'
1010
FLOAT = 'FLOAT'
11+
CELL = 'CELL'
1112
PLUS = 'PLUS'
1213
MINUS = 'MINUS'
1314
MULTIPLICATION = 'MULT'

lexer.py

+14
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ def keyword_tokenize(self):
101101
# Return appropriate token
102102
if alphanumerical_string == "is":
103103
return self.handle_multi_word_operator(alphanumerical_string)
104+
elif alphanumerical_string == "cell":
105+
return self.handle_excel_cell()
104106
elif alphanumerical_string in Dictionary.KEYWORDS:
105107
return Token(Dictionary.KEYWORD, alphanumerical_string)
106108
# It wasn't a keyword, so it must be an identifier
@@ -113,6 +115,18 @@ def peek(self):
113115
return self.input_string[self.position.index + 1]
114116
else:
115117
return None
118+
119+
# Handle Excel cell references (e.g. 'A1', 'B2', 'C3')
120+
def handle_excel_cell(self):
121+
cell_reference = self.peek_word_ahead()
122+
self.advance_n(len(cell_reference))
123+
cell_reference = cell_reference.strip()
124+
# an excel cell reference is any amount of alphabetic characters followed by any amount of numeric characters
125+
# validating this will be handled in the parser
126+
# advance the focus to the end of the cell reference
127+
return Token(Dictionary.CELL, cell_reference)
128+
129+
116130

117131
# TODO: Could potentially be made more bullet-proof in regards to syntax errors
118132
# Check for multi-word operators (e.g. 'is equal to', 'is greater than')

parser_nodes.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,14 @@ def __repr__(self):
5656

5757
def __str__(self):
5858
return self.__repr__()
59-
59+
60+
class CellReferenceNode(Node):
61+
"""Class for nodes that represent cell references."""
62+
def __init__(self, value):
63+
self.value = value
64+
65+
def __repr__(self):
66+
return f"CellReferenceNode({self.value})"
67+
68+
def __str__(self):
69+
return self.__repr__()

text_input.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
(2+3)*2
2-
if 1 is not equal to 1
3-
hello is yes
2+
if cell a1 is not equal to 1
3+
hello is yes

0 commit comments

Comments
 (0)