VARIABLESSSS

This commit is contained in:
SpookyDervish
2025-10-13 21:05:03 +11:00
parent 4e17674361
commit f9cd1dba29
17 changed files with 366 additions and 65 deletions

32
AST.py
View File

@@ -7,6 +7,7 @@ class NodeType(Enum):
# Statements
ExpressionStatement = "ExpressionStatement"
AssignmentStatement = "AssignmentStatement"
# Expressions
InfixExpression = "InfixExpression"
@@ -14,6 +15,7 @@ class NodeType(Enum):
# Literals
IntegerLiteral = "IntegerLiteral"
FloatLiteral = "FloatLiteral"
IdentifierLiteral = "IdentifierLiteral"
class Node:
@abstractmethod
@@ -56,6 +58,23 @@ class ExpressionStatement(Statement):
"type": self.type().value,
"expr": self.expr.json()
}
class AssignmentStatement(Statement):
def __init__(self, name: Expression = None, value: Expression = None, value_type: str = None) -> None:
self.name = name
self.value = value
self.value_type = value_type
def type(self) -> NodeType:
return NodeType.AssignmentStatement
def json(self) -> dict:
return {
"type": self.type().value,
"name": self.name.json(),
"value": self.value.json(),
"value_type": self.value_type
}
# endregion
# region Expressions
@@ -103,4 +122,17 @@ class FloatLiteral(Expression):
"type": self.type().value,
"value": self.value
}
class IdentifierLiteral(Expression):
def __init__(self, value: str = None) -> None:
self.value: str = value
def type(self) -> NodeType:
return NodeType.IdentifierLiteral
def json(self) -> dict:
return {
"type": self.type().value,
"value": self.value
}
# endregion

152
compiler.py Normal file
View File

@@ -0,0 +1,152 @@
from llvmlite import ir
from AST import Node, NodeType, Program, Expression
from AST import ExpressionStatement, AssignmentStatement
from AST import InfixExpression
from AST import IntegerLiteral, FloatLiteral, IdentifierLiteral
from environment import Environment
class Compiler:
def __init__(self) -> None:
self.type_map: dict[str, ir.type] = {
"bool": ir.IntType(1),
"byte": ir.IntType(8),
"short": ir.IntType(16),
"int": ir.IntType(32),
"long": ir.IntType(64),
"float": ir.FloatType(),
"double": ir.DoubleType()
}
self.module: ir.Module = ir.Module("main")
self.builder: ir.IRBuilder = ir.IRBuilder()
self.environment: Environment = Environment()
def compile(self, node: Node) -> None:
match node.type():
case NodeType.Program:
self.__visit_program(node)
# Statements
case NodeType.ExpressionStatement:
self.__visit_expression_statement(node)
case NodeType.AssignmentStatement:
self.__visit_assignment_statement(node)
# Expressions
case NodeType.InfixExpression:
self.__visit_infix_expression(node)
# region Visit Methods
def __visit_program(self, node: Program) -> None:
func_main: str = "main"
param_types: list[ir.Type] = []
return_type = ir.Type = self.type_map["int"]
fnty = ir.FunctionType(return_type, param_types)
func = ir.Function(self.module, fnty, func_main)
block = func.append_basic_block(f"{func_main}_entry")
self.builder = ir.IRBuilder(block)
for stmt in node.statements:
self.compile(stmt)
return_value: ir.Constant = ir.Constant(self.type_map["int"], 123)
self.builder.ret(return_value)
# region Statements
def __visit_expression_statement(self, node: ExpressionStatement) -> None:
self.compile(node.expr)
def __visit_assignment_statement(self, node: AssignmentStatement) -> None:
name: str = node.name.value
value: Expression = node.value
value_type: str = node.value_type # TODO: implemented
value, Type = self.__resolve_value(node=value)
if self.environment.lookup(name) is None:
# Define and allocate the new variable
ptr = self.builder.alloca(Type)
# Storing the value to the ptr
self.builder.store(value, ptr)
# Add the variable to the environment
self.environment.define(name, value, Type)
else:
ptr, _ = self.environment.lookup(name)
self.builder.store(value, ptr)
# endregion
# region Expressions
def __visit_infix_expression(self, node: InfixExpression) -> None:
operator: str = node.operator
left_value, left_type = self.__resolve_value(node.left_node)
right_value, right_type = self.__resolve_value(node.right_node)
value = None
Type = None
if isinstance(right_type, ir.IntType) and isinstance(left_type, ir.IntType):
Type = self.type_map["int"]
match operator:
case "+":
value = self.builder.add(left_value, right_value)
case "-":
value = self.builder.sub(left_value, right_value)
case "*":
value = self.builder.mul(left_value, right_value)
case "/":
value = self.builder.sdiv(left_value, right_value)
case "%":
value = self.builder.srem(left_value, right_value)
case "^":
# TODO
pass
elif isinstance(right_type, ir.FloatType) and isinstance(left_type, ir.FloatType):
Type = self.type_map["float"]
match operator:
case "+":
value = self.builder.fadd(left_value, right_value)
case "-":
value = self.builder.fsub(left_value, right_value)
case "*":
value = self.builder.fmul(left_value, right_value)
case "/":
value = self.builder.fdiv(left_value, right_value)
case "%":
value = self.builder.frem(left_value, right_value)
case "^":
# TODO
pass
return value, Type
# endregion
# endregion
# region Helper Methods
def __resolve_value(self, node: Expression) -> tuple[ir.Value, ir.Type]:
match node.type():
case NodeType.IntegerLiteral:
node: IntegerLiteral = node
value, Type = node.value, self.type_map['int']
return ir.Constant(Type, value), Type
case NodeType.FloatLiteral:
node: FloatLiteral = node
value, Type = node.value, self.type_map['float']
return ir.Constant(Type, value), Type
case NodeType.IdentifierLiteral:
node: IdentifierLiteral = node
ptr, Type = self.environment.lookup(node.value)
return self.builder.load(ptr), Type
# expression value
case NodeType.InfixExpression:
return self.__visit_infix_expression(node)
# endregion

View File

@@ -2,36 +2,17 @@
"type": "Program",
"statements": [
{
"ExpressionStatement": {
"type": "ExpressionStatement",
"expr": {
"type": "InfixExpression",
"left_node": {
"type": "InfixExpression",
"left_node": {
"type": "InfixExpression",
"left_node": {
"type": "IntegerLiteral",
"value": 5
},
"operator": "+",
"right_node": {
"type": "IntegerLiteral",
"value": 5
}
},
"operator": "*",
"right_node": {
"type": "IntegerLiteral",
"value": 3
}
},
"operator": "+",
"right_node": {
"type": "IntegerLiteral",
"value": 2
}
}
"AssignmentStatement": {
"type": "AssignmentStatement",
"name": {
"type": "IdentifierLiteral",
"value": "myVar"
},
"value": {
"type": "IntegerLiteral",
"value": 1
},
"value_type": "Bool"
}
}
]

13
debug/ir.ll Normal file
View File

@@ -0,0 +1,13 @@
; ModuleID = "main"
target triple = "x86_64-pc-windows-msvc"
target datalayout = ""
define i32 @"main"()
{
main_entry:
%".2" = alloca float
store float 0x3ff3ae1480000000, float* %".2"
%".4" = alloca i32
store i32 456, i32* %".4"
ret i32 123
}

23
environment.py Normal file
View File

@@ -0,0 +1,23 @@
from llvmlite import ir
class Environment:
def __init__(self, records: dict[str, tuple[ir.Value, ir.Type]] = None, parent = None, name: str = "global") -> None:
self.records: dict[str, tuple[ir.Value, ir.Type]] = records if records else {}
self.parent = parent
self.name: str = name
def define(self, name: str, value: ir.Value, _type: ir.Type) -> ir.Value:
self.records[name] = (value, _type)
return value
def lookup(self, name: str) -> tuple[ir.Value, ir.Type]:
return self.__resolve(name)
def __resolve(self, name: str) -> tuple[ir.Value, ir.Type]:
if name in self.records:
return self.records[name]
elif self.parent:
return self.parent.__resolve(name)
else:
return None

View File

@@ -1,4 +1,4 @@
from lexer_token import Token, TokenType
from lexer_token import Token, TokenType, lookup_ident
from typing import Any
@@ -36,6 +36,9 @@ class Lexer:
def __is_digit(self, char: str) -> bool:
return "0" <= char and char <= "9"
def __is_letter(self, char: str) -> bool:
return "a" <= char and char <= "z" or "A" <= char and char <= "Z" or char == "_"
def __read_number(self) -> Token:
start_pos: int = self.position
dot_count: int = 0
@@ -61,6 +64,13 @@ class Lexer:
else:
return self.__new_token(TokenType.FLOAT, float(output))
def __read_identifier(self) -> str:
position = self.position
while self.current_char is not None and (self.__is_letter(self.current_char) or self.current_char.isalnum()):
self.__read_char()
return self.source[position:self.position]
def next_token(self) -> Token:
tok: Token = None
@@ -79,6 +89,8 @@ class Lexer:
tok = self.__new_token(TokenType.POW, self.current_char)
case "%":
tok = self.__new_token(TokenType.MODULUS, self.current_char)
case "=":
tok = self.__new_token(TokenType.EQ, self.current_char)
case "(":
tok = self.__new_token(TokenType.LPAREN, self.current_char)
case ")":
@@ -98,6 +110,12 @@ class Lexer:
case None:
tok = self.__new_token(TokenType.EOF, "")
case _:
if self.__is_letter(self.current_char):
literal: str = self.__read_identifier()
tt: TokenType = lookup_ident(literal)
tok = self.__new_token(tt, literal)
return tok
if self.__is_digit(self.current_char):
tok = self.__read_number()
return tok

View File

@@ -8,6 +8,7 @@ class TokenType(Enum):
ILLEGAL = "ILLEGAL"
# Data types
IDENT = "IDENT"
INT = "INT"
FLOAT = "FLOAT"
@@ -19,6 +20,9 @@ class TokenType(Enum):
POW = "POW"
MODULUS = "MODULUS"
# Assignment symbols
EQ = "EQ"
# Symbols
LPAREN = "LPAREN"
RPAREN = "RPAREN"
@@ -29,6 +33,11 @@ class TokenType(Enum):
COLON = "COLON"
SEMICOLON = "SEMICOLON"
# Keywords
# Typing
TYPE = "TYPE"
class Token:
def __init__(self, type: TokenType, literal: Any, line_no: int, position: int) -> None:
self.type = type
@@ -40,4 +49,29 @@ class Token:
return f"token[{self.type} : {self.literal} : Line {self.line_no} : Position {self.position}]"
def __repr__(self) -> str:
return str(self)
return str(self)
KEYWORDS: dict[str, TokenType] = {
}
ALT_KEYWORDS: dict[str, TokenType] = {
}
TYPE_KEYWORDS: list[str] = ["Int", "Float", "String", "Bool", "List", "Nil"]
def lookup_ident(ident: str) -> TokenType:
tt: TokenType | None = KEYWORDS.get(ident)
if tt is not None:
return tt
tt: TokenType | None = ALT_KEYWORDS.get(ident)
if tt is not None:
return tt
if ident in TYPE_KEYWORDS:
return TokenType.TYPE
return TokenType.IDENT

32
main.py
View File

@@ -1,14 +1,20 @@
from lexer import Lexer
from plasma_parser import Parser
from compiler import Compiler
from AST import Program
import json
LEXER_DEBUG: bool = True
PARSER_DEBUG: bool = True
from llvmlite import ir
from llvmlite.binding import targets
from ctypes import CFUNCTYPE, c_int, c_float
LEXER_DEBUG: bool = False
PARSER_DEBUG: bool = False
COMPILER_DEBUG: bool = True
if __name__ == "__main__":
with open("tests/parser.pla") as f:
with open("tests/test.pla") as f:
code: str = f.read()
if LEXER_DEBUG:
@@ -19,11 +25,27 @@ if __name__ == "__main__":
l: Lexer = Lexer(source=code)
p: Parser = Parser(lexer=l)
program: Program = p.parse_program()
if len(p.errors) > 0:
for err in p.errors:
print(err)
exit(1)
if PARSER_DEBUG:
print("===== PARSER DEBUG =====")
program: Program = p.parse_program()
#program: Program = p.parse_program()
with open("debug/ast.json", "w") as f:
json.dump(program.json(), f, indent=4)
print("Wrote AST to debug/ast.json successfully.")
print("Wrote AST to debug/ast.json successfully.")
c: Compiler = Compiler()
c.compile(program)
module: ir.Module = c.module
module.triple = targets.get_default_triple()
if COMPILER_DEBUG:
with open("debug/ir.ll", "w") as f:
f.write(str(module))

View File

@@ -4,9 +4,9 @@ from typing import Callable
from enum import Enum, auto
from AST import Statement, Expression, Program
from AST import ExpressionStatement
from AST import ExpressionStatement, AssignmentStatement
from AST import InfixExpression
from AST import IntegerLiteral, FloatLiteral
from AST import IntegerLiteral, FloatLiteral, IdentifierLiteral
class PrecedenceType(Enum):
P_LOWEST = 0
@@ -63,6 +63,9 @@ class Parser:
self.current_token = self.peek_token
self.peek_token = self.lexer.next_token()
def __current_token_is(self, tt: TokenType) -> bool:
return self.current_token.type == tt
def __peek_token_is(self, tt: TokenType) -> bool:
return self.peek_token.type == tt
@@ -108,7 +111,11 @@ class Parser:
# region Statement Methods
def __parse_statement(self) -> Statement:
return self.__parse_expression_statement()
match self.current_token.type:
case TokenType.IDENT:
return self.__parse_assignment_statement()
case _:
return self.__parse_expression_statement()
def __parse_expression_statement(self) -> ExpressionStatement:
expr = self.__parse_expression(PrecedenceType.P_LOWEST)
@@ -119,6 +126,30 @@ class Parser:
stmt: ExpressionStatement = ExpressionStatement(expr=expr)
return stmt
def __parse_assignment_statement(self) -> AssignmentStatement:
# x: Int = 10;
stmt: AssignmentStatement = AssignmentStatement(name=IdentifierLiteral(self.current_token.literal))
if not self.__expect_peek(TokenType.COLON):
return None
if not self.__expect_peek(TokenType.TYPE):
return None
stmt.value_type = self.current_token.literal
if not self.__expect_peek(TokenType.EQ):
return None
self.__next_token()
stmt.value = self.__parse_expression(PrecedenceType.P_LOWEST)
while not self.__current_token_is(TokenType.SEMICOLON) and not self.__current_token_is(TokenType.EOF):
self.__next_token()
return stmt
# endregion
# region Expression Methods

View File

@@ -1,9 +1,9 @@
depend "io.pla"
if (1 + 2 == 3) {
print("The universe is functional!")
print("The universe is functional!");
}
unless
{
print("WHAT, HOW")
print("WHAT, HOW");
}

View File

@@ -2,7 +2,7 @@ depend "io.pla"
depend "string.pla"
add = Func(a: Int, b: Int): Int {
return a + b
return a + b;
}
print(String(add(1, 3)))
print(String(add(1, 3)));

View File

@@ -1,2 +1,2 @@
depend "io.pla"
print("Hello, World!")
print("Hello, World!");

View File

@@ -1,6 +0,0 @@
123
0.456
[]
{}
(1 + 3 * 2 ^ 4) % 2
2 - 1

View File

@@ -1 +0,0 @@
(5 + 5) * 3 + 2

2
tests/test.pla Normal file
View File

@@ -0,0 +1,2 @@
a: Float = 1.23;
b: Int = 456;

View File

@@ -14,6 +14,6 @@ struct Person {
speak = Func(sentence: String): Nil {
print(sentence)
}
}
max: Person = {"Max", 17, }
max: Person = {"Max", 17, speak, Gender.male};

View File

@@ -1,16 +1,16 @@
depend "string.pla"
depend "io.h"
myInt: Int = 123
myDecimal: Float = 0.456
myBoolean: Bool = true
myString: String = "Hello!\n"
myList: List = [1, "hi", true, [1, 2, 3], 0.789]
myInt: Int = 123;
myDecimal: Float = 0.456;
myBoolean: Bool = true;
myString: String = "Hello!\n";
myList: List = [1, "hi", true, [1, 2, 3], 0.789];
MY_CONSTANT: Const(String) = "foo bar"
MY_CONSTANT: Const(String) = "foo bar";
print(String(myInt))
print(String(myDecimal))
print(String(myBoolean))
print(myString)
print(String(myList))
print(String(myInt));
print(String(myDecimal));
print(String(myBoolean));
print(myString);
print(String(myList));