commit 39789bd96ad190e8e5f0da238f7db417ec75f47d Author: Alvin <524715@vistacollege.nl> Date: Tue Jul 22 15:56:40 2025 +0200 Initial commit: Basic interpreter structure diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..81b41a2 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,3 @@ +# Femcode + +A femboy-themed programming language. \ No newline at end of file diff --git a/examples/hello_world.fem b/examples/hello_world.fem new file mode 100644 index 0000000..410b0aa --- /dev/null +++ b/examples/hello_world.fem @@ -0,0 +1 @@ +UwU Boy "Hello, Femboy World!" \ No newline at end of file diff --git a/src/__pycache__/interpreter.cpython-313.pyc b/src/__pycache__/interpreter.cpython-313.pyc new file mode 100644 index 0000000..0470a69 Binary files /dev/null and b/src/__pycache__/interpreter.cpython-313.pyc differ diff --git a/src/__pycache__/lexer.cpython-313.pyc b/src/__pycache__/lexer.cpython-313.pyc new file mode 100644 index 0000000..36252f4 Binary files /dev/null and b/src/__pycache__/lexer.cpython-313.pyc differ diff --git a/src/__pycache__/parser.cpython-313.pyc b/src/__pycache__/parser.cpython-313.pyc new file mode 100644 index 0000000..6bd80ea Binary files /dev/null and b/src/__pycache__/parser.cpython-313.pyc differ diff --git a/src/interpreter.py b/src/interpreter.py new file mode 100644 index 0000000..ef3dd9d --- /dev/null +++ b/src/interpreter.py @@ -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) diff --git a/src/lexer.py b/src/lexer.py new file mode 100644 index 0000000..e6aa2a5 --- /dev/null +++ b/src/lexer.py @@ -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 diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..404c4ed --- /dev/null +++ b/src/main.py @@ -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() diff --git a/src/parser.py b/src/parser.py new file mode 100644 index 0000000..8c8a731 --- /dev/null +++ b/src/parser.py @@ -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