Initial commit: Basic interpreter structure

This commit is contained in:
Alvin
2025-07-22 15:56:40 +02:00
commit 39789bd96a
9 changed files with 128 additions and 0 deletions

3
docs/README.md Normal file
View File

@@ -0,0 +1,3 @@
# Femcode
A femboy-themed programming language.

1
examples/hello_world.fem Normal file
View File

@@ -0,0 +1 @@
UwU Boy "Hello, Femboy World!"

Binary file not shown.

Binary file not shown.

Binary file not shown.

18
src/interpreter.py Normal file
View File

@@ -0,0 +1,18 @@
class Interpreter:
def __init__(self, ast):
self.ast = ast
def interpret(self):
for node in self.ast:
self.visit(node)
def visit(self, node):
method_name = f'visit_{type(node).__name__}'
method = getattr(self, method_name, self.no_visit_method)
return method(node)
def no_visit_method(self, node):
raise Exception(f'No visit_{type(node).__name__} method defined')
def visit_Print(self, node):
print(node.value)

51
src/lexer.py Normal file
View File

@@ -0,0 +1,51 @@
import re
class Token:
def __init__(self, type, value):
self.type = type
self.value = value
def __repr__(self):
return f'Token({self.type}, {self.value!r})'
class Lexer:
def __init__(self, text):
self.text = text
self.pos = 0
def error(self):
raise Exception('Invalid character')
def get_next_token(self):
if self.pos > len(self.text) - 1:
return Token('EOF', None)
current_char = self.text[self.pos]
if current_char.isspace():
self.pos += 1
return self.get_next_token()
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
return Token('STRING', string)
if re.match(r'\bUwU Boy\b', self.text[self.pos:]):
self.pos += 7
return Token('PRINT', 'UwU Boy')
self.error()
def tokenize(self):
tokens = []
while True:
token = self.get_next_token()
tokens.append(token)
if token.type == 'EOF':
break
return tokens

19
src/main.py Normal file
View File

@@ -0,0 +1,19 @@
from lexer import Lexer
from parser import Parser
from interpreter import Interpreter
def main():
with open('../examples/hello_world.fem', 'r') as f:
text = f.read()
lexer = Lexer(text)
tokens = lexer.tokenize()
parser = Parser(tokens)
ast = parser.parse()
interpreter = Interpreter(ast)
interpreter.interpret()
if __name__ == '__main__':
main()

36
src/parser.py Normal file
View File

@@ -0,0 +1,36 @@
class AST:
pass
class Print(AST):
def __init__(self, value):
self.value = value
class Parser:
def __init__(self, tokens):
self.tokens = tokens
self.pos = 0
def get_next_token(self):
if self.pos < len(self.tokens):
token = self.tokens[self.pos]
self.pos += 1
return token
return None
def parse(self):
statements = []
while True:
token = self.get_next_token()
if token is None or token.type == 'EOF':
break
if token.type == 'PRINT':
next_token = self.get_next_token()
if next_token.type == 'STRING':
statements.append(Print(next_token.value))
else:
raise Exception("Expected a string after 'UwU Boy'")
else:
raise Exception(f"Unexpected token: {token.type}")
return statements