feat: Add numbers and arithmetic operations

This commit is contained in:
Alvin
2025-07-22 16:12:23 +02:00
parent ad1d46b6e7
commit 735ae8e068
4 changed files with 95 additions and 13 deletions

3
examples/arithmetic.fem Normal file
View File

@@ -0,0 +1,3 @@
my_number is 10 + 5 * 2
UwU Boy my_number
UwU Boy 20 / 4 - 1

View File

@@ -1,6 +1,7 @@
class Interpreter: class Interpreter:
def __init__(self, ast): def __init__(self, ast):
self.ast = ast self.ast = ast
self.variables = {}
def interpret(self): def interpret(self):
for node in self.ast: for node in self.ast:
@@ -14,5 +15,31 @@ class Interpreter:
def no_visit_method(self, node): def no_visit_method(self, node):
raise Exception(f'No visit_{type(node).__name__} method defined') raise Exception(f'No visit_{type(node).__name__} method defined')
def visit_Number(self, node):
return node.value
def visit_BinOp(self, node):
if node.op.type == 'PLUS':
return self.visit(node.left) + self.visit(node.right)
elif node.op.type == 'MINUS':
return self.visit(node.left) - self.visit(node.right)
elif node.op.type == 'MUL':
return self.visit(node.left) * self.visit(node.right)
elif node.op.type == 'DIV':
return self.visit(node.left) / self.visit(node.right)
def visit_Print(self, node): def visit_Print(self, node):
print(node.value) value_to_print = self.visit(node.value)
print(value_to_print)
def visit_Assign(self, node):
var_name = node.left.value
value = self.visit(node.right)
self.variables[var_name] = value
def visit_Variable(self, node):
var_name = node.value
if var_name in self.variables:
return self.variables[var_name]
else:
raise NameError(f"name '{var_name}' is not defined")

View File

@@ -38,6 +38,25 @@ class Lexer:
self.pos = string_end + 1 self.pos = string_end + 1
return Token('STRING', string) return Token('STRING', string)
if current_char.isdigit():
start_pos = self.pos
while self.pos < len(self.text) and self.text[self.pos].isdigit():
self.pos += 1
return Token('INTEGER', int(self.text[start_pos:self.pos]))
if current_char == '+':
self.pos += 1
return Token('PLUS', '+')
if current_char == '-':
self.pos += 1
return Token('MINUS', '-')
if current_char == '*':
self.pos += 1
return Token('MUL', '*')
if current_char == '/':
self.pos += 1
return Token('DIV', '/')
# Match keywords # Match keywords
if re.match(r'\bUwU Boy\b', self.text[self.pos:]): if re.match(r'\bUwU Boy\b', self.text[self.pos:]):
self.pos += 7 self.pos += 7

View File

@@ -1,6 +1,17 @@
class AST: class AST:
pass pass
class Number(AST):
def __init__(self, token):
self.token = token
self.value = token.value
class BinOp(AST):
def __init__(self, left, op, right):
self.left = left
self.token = self.op = op
self.right = right
class Print(AST): class Print(AST):
def __init__(self, value): def __init__(self, value):
self.value = value self.value = value
@@ -48,19 +59,19 @@ class Parser:
if token.type == 'ID' and self.pos + 1 < len(self.tokens) and self.tokens[self.pos + 1].type == 'ASSIGN': if token.type == 'ID' and self.pos + 1 < len(self.tokens) and self.tokens[self.pos + 1].type == 'ASSIGN':
return self.parse_assignment_statement() return self.parse_assignment_statement()
elif token.type == 'ID': elif token.type == 'ID':
raise Exception(f"Unexpected identifier '{token.value}' without assignment.") # This case should be handled by expression parsing if it's part of an expression
# For now, raise an error if it's a standalone ID not part of assignment or print
raise Exception(f"Unexpected identifier '{token.value}' without assignment or print.")
# Handle expressions as statements (e.g., just a number or an arithmetic operation)
# This might need refinement depending on what we consider a valid standalone statement
# For now, let's assume expressions are only part of print or assignment
raise Exception(f"Invalid statement starting with token {token.type}") raise Exception(f"Invalid statement starting with token {token.type}")
def parse_print_statement(self): def parse_print_statement(self):
self.get_next_token() # Consume PRINT token self.get_next_token() # Consume PRINT token
token = self.get_next_token() expr = self.expression()
if token.type == 'STRING': return Print(expr)
return Print(token.value)
elif token.type == 'ID':
return Print(Variable(token))
else:
raise Exception("Expected a string or variable after 'UwU Boy'")
def parse_assignment_statement(self): def parse_assignment_statement(self):
var_token = self.get_next_token() var_token = self.get_next_token()
@@ -68,8 +79,30 @@ class Parser:
assign_token = self.get_next_token() assign_token = self.get_next_token()
value_token = self.get_next_token() right_expr = self.expression()
if value_token.type == 'STRING': return Assign(left=var_node, op=assign_token, right=right_expr)
return Assign(left=var_node, op=assign_token, right=value_token.value)
def factor(self):
token = self.get_next_token()
if token.type == 'INTEGER':
return Number(token)
elif token.type == 'STRING':
return token.value # Strings are literals, not AST nodes for now
elif token.type == 'ID':
return Variable(token)
else: else:
raise Exception("Expected a string value for assignment") raise Exception(f"Expected integer, string or identifier, got {token.type}")
def term(self):
node = self.factor()
while self.peek_next_token().type in ('MUL', 'DIV'):
token = self.get_next_token()
node = BinOp(left=node, op=token, right=self.factor())
return node
def expression(self):
node = self.term()
while self.peek_next_token().type in ('PLUS', 'MINUS'):
token = self.get_next_token()
node = BinOp(left=node, op=token, right=self.term())
return node