mirror of
https://github.com/Alvin-Zilverstand/femcode.git
synced 2026-03-06 11:06:47 +01:00
feat: Implement functions (definition and calls)
This commit is contained in:
5
examples/functions.fem
Normal file
5
examples/functions.fem
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Femboy say_hello Femboycore
|
||||||
|
UwU Boy "Hello from function!"
|
||||||
|
Periodt
|
||||||
|
|
||||||
|
say_hello()
|
||||||
@@ -2,6 +2,7 @@ class Interpreter:
|
|||||||
def __init__(self, ast):
|
def __init__(self, ast):
|
||||||
self.ast = ast
|
self.ast = ast
|
||||||
self.variables = {}
|
self.variables = {}
|
||||||
|
self.functions = {}
|
||||||
|
|
||||||
def interpret(self):
|
def interpret(self):
|
||||||
for node in self.ast:
|
for node in self.ast:
|
||||||
@@ -79,3 +80,31 @@ class Interpreter:
|
|||||||
def visit_WhileStatement(self, node):
|
def visit_WhileStatement(self, node):
|
||||||
while self.visit(node.condition):
|
while self.visit(node.condition):
|
||||||
self.visit(node.body)
|
self.visit(node.body)
|
||||||
|
|
||||||
|
def visit_FunctionDefinition(self, node):
|
||||||
|
self.functions[node.name] = {
|
||||||
|
'parameters': node.parameters,
|
||||||
|
'body': node.body
|
||||||
|
}
|
||||||
|
|
||||||
|
def visit_FunctionCall(self, node):
|
||||||
|
func_name = node.name
|
||||||
|
if func_name not in self.functions:
|
||||||
|
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
|
||||||
|
|
||||||
|
# Execute function body
|
||||||
|
try:
|
||||||
|
self.visit(func_info['body'])
|
||||||
|
except ReturnValue as e:
|
||||||
|
return e.value
|
||||||
|
|
||||||
|
def visit_ReturnStatement(self, node):
|
||||||
|
raise ReturnValue(self.visit(node.value))
|
||||||
|
|
||||||
|
class ReturnValue(Exception):
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
|||||||
14
src/lexer.py
14
src/lexer.py
@@ -44,6 +44,14 @@ class Lexer:
|
|||||||
self.pos += 1
|
self.pos += 1
|
||||||
return Token('INTEGER', int(self.text[start_pos:self.pos]))
|
return Token('INTEGER', int(self.text[start_pos:self.pos]))
|
||||||
|
|
||||||
|
# Parentheses
|
||||||
|
if current_char == '(':
|
||||||
|
self.pos += 1
|
||||||
|
return Token('LPAREN', '(')
|
||||||
|
if current_char == ')':
|
||||||
|
self.pos += 1
|
||||||
|
return Token('RPAREN', ')')
|
||||||
|
|
||||||
# Operators
|
# Operators
|
||||||
if current_char == '+':
|
if current_char == '+':
|
||||||
self.pos += 1
|
self.pos += 1
|
||||||
@@ -93,6 +101,12 @@ class Lexer:
|
|||||||
if re.match(r'\bOtokonoko\b', self.text[self.pos:]):
|
if re.match(r'\bOtokonoko\b', self.text[self.pos:]):
|
||||||
self.pos += len('Otokonoko')
|
self.pos += len('Otokonoko')
|
||||||
return Token('OTOKONOKO', 'Otokonoko')
|
return Token('OTOKONOKO', 'Otokonoko')
|
||||||
|
if re.match(r'\bFemboy\b', self.text[self.pos:]):
|
||||||
|
self.pos += len('Femboy')
|
||||||
|
return Token('FUNCTION_DEF', 'Femboy')
|
||||||
|
if re.match(r'\bFemme\b', self.text[self.pos:]):
|
||||||
|
self.pos += len('Femme')
|
||||||
|
return Token('RETURN', 'Femme')
|
||||||
if re.match(r'\bis\b', self.text[self.pos:]):
|
if re.match(r'\bis\b', self.text[self.pos:]):
|
||||||
self.pos += 2
|
self.pos += 2
|
||||||
return Token('ASSIGN', 'is')
|
return Token('ASSIGN', 'is')
|
||||||
|
|||||||
@@ -53,6 +53,21 @@ class WhileStatement(AST):
|
|||||||
self.condition = condition
|
self.condition = condition
|
||||||
self.body = body
|
self.body = body
|
||||||
|
|
||||||
|
class FunctionDefinition(AST):
|
||||||
|
def __init__(self, name, parameters, body):
|
||||||
|
self.name = name
|
||||||
|
self.parameters = parameters
|
||||||
|
self.body = body
|
||||||
|
|
||||||
|
class FunctionCall(AST):
|
||||||
|
def __init__(self, name, arguments):
|
||||||
|
self.name = name
|
||||||
|
self.arguments = arguments
|
||||||
|
|
||||||
|
class ReturnStatement(AST):
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
class Parser:
|
class Parser:
|
||||||
def __init__(self, tokens):
|
def __init__(self, tokens):
|
||||||
self.tokens = tokens
|
self.tokens = tokens
|
||||||
@@ -82,8 +97,15 @@ class Parser:
|
|||||||
if token.type == 'PRINT':
|
if token.type == 'PRINT':
|
||||||
return self.parse_print_statement()
|
return self.parse_print_statement()
|
||||||
|
|
||||||
if token.type == 'ID' and self.pos + 1 < len(self.tokens) and self.tokens[self.pos + 1].type == 'ASSIGN':
|
if token.type == 'ID':
|
||||||
|
# Check for assignment
|
||||||
|
if self.pos + 1 < len(self.tokens) and self.tokens[self.pos + 1].type == 'ASSIGN':
|
||||||
return self.parse_assignment_statement()
|
return self.parse_assignment_statement()
|
||||||
|
# Check for function call as a statement
|
||||||
|
if self.pos + 1 < len(self.tokens) and self.tokens[self.pos + 1].type == 'LPAREN':
|
||||||
|
# Consume the ID token first, then parse the function call
|
||||||
|
name_token = self.get_next_token()
|
||||||
|
return self.parse_function_call(name_token)
|
||||||
|
|
||||||
if token.type == 'FEMBOY_FEMININE':
|
if token.type == 'FEMBOY_FEMININE':
|
||||||
return self.parse_if_statement()
|
return self.parse_if_statement()
|
||||||
@@ -91,6 +113,12 @@ class Parser:
|
|||||||
if token.type == 'OTOKONOKO':
|
if token.type == 'OTOKONOKO':
|
||||||
return self.parse_while_statement()
|
return self.parse_while_statement()
|
||||||
|
|
||||||
|
if token.type == 'FUNCTION_DEF':
|
||||||
|
return self.parse_function_definition()
|
||||||
|
|
||||||
|
if token.type == 'RETURN':
|
||||||
|
return self.parse_return_statement()
|
||||||
|
|
||||||
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):
|
||||||
@@ -162,6 +190,34 @@ class Parser:
|
|||||||
|
|
||||||
return WhileStatement(condition, body)
|
return WhileStatement(condition, body)
|
||||||
|
|
||||||
|
def parse_function_definition(self):
|
||||||
|
self.get_next_token() # Consume FUNCTION_DEF
|
||||||
|
name_token = self.get_next_token()
|
||||||
|
if name_token.type != 'ID':
|
||||||
|
raise Exception("Expected function name (ID)")
|
||||||
|
|
||||||
|
# For simplicity, assume no parameters for now
|
||||||
|
parameters = []
|
||||||
|
|
||||||
|
if self.peek_next_token().type != 'FEMBOYCORE':
|
||||||
|
raise Exception("Expected 'Femboycore' to start function body")
|
||||||
|
self.get_next_token() # Consume FEMBOYCORE
|
||||||
|
|
||||||
|
body_statements = []
|
||||||
|
while self.peek_next_token().type != 'PERIODT':
|
||||||
|
if self.peek_next_token().type == 'EOF':
|
||||||
|
raise Exception("Unterminated function definition: Expected 'Periodt'")
|
||||||
|
body_statements.append(self.parse_statement())
|
||||||
|
self.get_next_token() # Consume PERIODT
|
||||||
|
body = Block(body_statements)
|
||||||
|
|
||||||
|
return FunctionDefinition(name_token.value, parameters, body)
|
||||||
|
|
||||||
|
def parse_return_statement(self):
|
||||||
|
self.get_next_token() # Consume RETURN
|
||||||
|
value = self.expression()
|
||||||
|
return ReturnStatement(value)
|
||||||
|
|
||||||
def factor(self):
|
def factor(self):
|
||||||
token = self.get_next_token()
|
token = self.get_next_token()
|
||||||
if token.type == 'INTEGER':
|
if token.type == 'INTEGER':
|
||||||
@@ -169,6 +225,9 @@ class Parser:
|
|||||||
elif token.type == 'STRING':
|
elif token.type == 'STRING':
|
||||||
return String(token) # Now returns a String AST node
|
return String(token) # Now returns a String AST node
|
||||||
elif token.type == 'ID':
|
elif token.type == 'ID':
|
||||||
|
# Check for function call
|
||||||
|
if self.peek_next_token().value == '(': # Assuming '(' is the next token for a function call
|
||||||
|
return self.parse_function_call(token)
|
||||||
return Variable(token)
|
return Variable(token)
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Expected integer, string or identifier, got {token.type}")
|
raise Exception(f"Expected integer, string or identifier, got {token.type}")
|
||||||
@@ -195,3 +254,13 @@ class Parser:
|
|||||||
node = Comparison(left=node, op=op_token, right=right_node)
|
node = Comparison(left=node, op=op_token, right=right_node)
|
||||||
|
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
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")
|
||||||
|
return FunctionCall(name_token.value, arguments)
|
||||||
|
|||||||
Reference in New Issue
Block a user