mirror of
https://github.com/Alvin-Zilverstand/femcode.git
synced 2026-03-06 11:06:47 +01:00
feat: Add numbers and arithmetic operations
This commit is contained in:
3
examples/arithmetic.fem
Normal file
3
examples/arithmetic.fem
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
my_number is 10 + 5 * 2
|
||||||
|
UwU Boy my_number
|
||||||
|
UwU Boy 20 / 4 - 1
|
||||||
@@ -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")
|
||||||
|
|||||||
19
src/lexer.py
19
src/lexer.py
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user