mirror of
https://github.com/Alvin-Zilverstand/femcode.git
synced 2026-03-06 13:23:39 +01:00
Initial commit: Basic interpreter structure
This commit is contained in:
3
docs/README.md
Normal file
3
docs/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Femcode
|
||||||
|
|
||||||
|
A femboy-themed programming language.
|
||||||
1
examples/hello_world.fem
Normal file
1
examples/hello_world.fem
Normal file
@@ -0,0 +1 @@
|
|||||||
|
UwU Boy "Hello, Femboy World!"
|
||||||
BIN
src/__pycache__/interpreter.cpython-313.pyc
Normal file
BIN
src/__pycache__/interpreter.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/__pycache__/lexer.cpython-313.pyc
Normal file
BIN
src/__pycache__/lexer.cpython-313.pyc
Normal file
Binary file not shown.
BIN
src/__pycache__/parser.cpython-313.pyc
Normal file
BIN
src/__pycache__/parser.cpython-313.pyc
Normal file
Binary file not shown.
18
src/interpreter.py
Normal file
18
src/interpreter.py
Normal 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
51
src/lexer.py
Normal 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
19
src/main.py
Normal 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
36
src/parser.py
Normal 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
|
||||||
Reference in New Issue
Block a user