mirror of
https://github.com/Alvin-Zilverstand/femcode.git
synced 2026-03-06 13:23:39 +01:00
feat: Implement function parameters and return values
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
class Interpreter:
|
||||
def __init__(self, ast):
|
||||
self.ast = ast
|
||||
self.variables = {}
|
||||
self.scope_stack = [{}]
|
||||
self.functions = {}
|
||||
|
||||
@property
|
||||
def current_scope(self):
|
||||
return self.scope_stack[-1]
|
||||
|
||||
def interpret(self):
|
||||
for node in self.ast:
|
||||
self.visit(node)
|
||||
@@ -57,14 +61,15 @@ class Interpreter:
|
||||
def visit_Assign(self, node):
|
||||
var_name = node.left.value
|
||||
value = self.visit(node.right)
|
||||
self.variables[var_name] = value
|
||||
self.current_scope[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")
|
||||
# Search up the scope stack for the variable
|
||||
for scope in reversed(self.scope_stack):
|
||||
if var_name in scope:
|
||||
return scope[var_name]
|
||||
raise NameError(f"name '{var_name}' is not defined")
|
||||
|
||||
def visit_Block(self, node):
|
||||
for statement in node.statements:
|
||||
@@ -93,14 +98,27 @@ class Interpreter:
|
||||
raise NameError(f"Function '{func_name}' is not defined")
|
||||
|
||||
func_info = self.functions[func_name]
|
||||
# For simplicity, no arguments are passed for now
|
||||
# In a real interpreter, you'd push a new scope and bind arguments to parameters
|
||||
|
||||
# Evaluate arguments
|
||||
evaluated_arguments = [self.visit(arg) for arg in node.arguments]
|
||||
|
||||
# Create a new scope for the function call
|
||||
new_scope = {}
|
||||
for i, param_name in enumerate(func_info['parameters']):
|
||||
new_scope[param_name] = evaluated_arguments[i]
|
||||
|
||||
self.scope_stack.append(new_scope)
|
||||
|
||||
# Execute function body
|
||||
try:
|
||||
self.visit(func_info['body'])
|
||||
except ReturnValue as e:
|
||||
self.scope_stack.pop() # Pop the function scope
|
||||
return e.value
|
||||
finally:
|
||||
# Ensure scope is popped even if no return or an error occurs
|
||||
if len(self.scope_stack) > 1: # Don't pop global scope
|
||||
self.scope_stack.pop()
|
||||
|
||||
def visit_ReturnStatement(self, node):
|
||||
raise ReturnValue(self.visit(node.value))
|
||||
|
||||
17
src/lexer.py
17
src/lexer.py
@@ -31,11 +31,13 @@ class Lexer:
|
||||
|
||||
if current_char == '"':
|
||||
self.pos += 1
|
||||
string_end = self.text.find('"', self.pos)
|
||||
if string_end == -1:
|
||||
self.error()
|
||||
string = self.text[self.pos:string_end]
|
||||
self.pos = string_end + 1
|
||||
start_string = self.pos
|
||||
while self.pos < len(self.text) and self.text[self.pos] != '"':
|
||||
self.pos += 1
|
||||
if self.pos == len(self.text):
|
||||
self.error() # Unterminated string
|
||||
string = self.text[start_string:self.pos]
|
||||
self.pos += 1 # Consume closing quote
|
||||
return Token('STRING', string)
|
||||
|
||||
if current_char.isdigit():
|
||||
@@ -65,6 +67,9 @@ class Lexer:
|
||||
if current_char == '/':
|
||||
self.pos += 1
|
||||
return Token('DIV', '/')
|
||||
if current_char == ',':
|
||||
self.pos += 1
|
||||
return Token('COMMA', ',')
|
||||
if current_char == '=':
|
||||
if self.pos + 1 < len(self.text) and self.text[self.pos + 1] == '=':
|
||||
self.pos += 2
|
||||
@@ -133,4 +138,4 @@ class Lexer:
|
||||
tokens.append(token)
|
||||
if token.type == 'EOF':
|
||||
break
|
||||
return tokens
|
||||
return tokens
|
||||
@@ -196,8 +196,25 @@ class Parser:
|
||||
if name_token.type != 'ID':
|
||||
raise Exception("Expected function name (ID)")
|
||||
|
||||
# For simplicity, assume no parameters for now
|
||||
print(f"After function name: {self.peek_next_token()}")
|
||||
|
||||
# Parse parameters
|
||||
parameters = []
|
||||
if self.peek_next_token().type == 'LPAREN':
|
||||
self.get_next_token() # Consume '('
|
||||
print(f"After LPAREN: {self.peek_next_token()}")
|
||||
while self.peek_next_token().type != 'RPAREN':
|
||||
param_token = self.get_next_token()
|
||||
if param_token.type != 'ID':
|
||||
raise Exception("Expected parameter name (ID)")
|
||||
parameters.append(param_token.value)
|
||||
print(f"After parameter {param_token.value}: {self.peek_next_token()}")
|
||||
if self.peek_next_token().type == 'COMMA':
|
||||
self.get_next_token() # Consume ','
|
||||
print(f"After COMMA: {self.peek_next_token()}")
|
||||
# No 'elif' here, the loop condition handles the RPAREN
|
||||
self.get_next_token() # Consume ')'
|
||||
print(f"After RPAREN: {self.peek_next_token()}")
|
||||
|
||||
if self.peek_next_token().type != 'FEMBOYCORE':
|
||||
raise Exception("Expected 'Femboycore' to start function body")
|
||||
@@ -258,9 +275,12 @@ class Parser:
|
||||
def parse_function_call(self, name_token):
|
||||
self.get_next_token() # Consume '('
|
||||
arguments = []
|
||||
# For simplicity, assume no arguments for now
|
||||
if self.peek_next_token().value == ')':
|
||||
self.get_next_token() # Consume ')'
|
||||
else:
|
||||
raise Exception("Expected ')' after function call")
|
||||
if self.peek_next_token().type != 'RPAREN':
|
||||
while True:
|
||||
arguments.append(self.expression())
|
||||
if self.peek_next_token().type == 'COMMA':
|
||||
self.get_next_token() # Consume ','
|
||||
else:
|
||||
break # Exit loop if not COMMA (implies RPAREN or EOF)
|
||||
self.get_next_token() # Consume ')'
|
||||
return FunctionCall(name_token.value, arguments)
|
||||
|
||||
Reference in New Issue
Block a user