file imports work!!!!!

This commit is contained in:
SpookyDervish
2025-10-18 07:21:19 +11:00
parent 24fcbb3fb7
commit 52686fa314
15 changed files with 210 additions and 73 deletions

35
AST.py
View File

@@ -17,11 +17,13 @@ class NodeType(Enum):
BreakStatement = "BreakStatement"
ContinueStatement = "ContinueStatement"
ForStatement = "ForStatement"
DependStatement = "DependStatement"
# Expressions
InfixExpression = "InfixExpression"
CallExpression = "CallExpression"
PrefixExpression = "PrefixExpression"
PostfixExpression = "PostfixExpression"
# Literals
IntegerLiteral = "IntegerLiteral"
@@ -295,7 +297,7 @@ class ContinueStatement(Statement):
}
class ForStatement(Statement):
def __init__(self, var_declaration: AssignmentStatement = None, condition: Expression = None, action: ReassignStatement = None, body: BlockStatement = None) -> None:
def __init__(self, var_declaration: AssignmentStatement = None, condition: Expression = None, action: Expression = None, body: BlockStatement = None) -> None:
self.var_declaration = var_declaration
self.condition = condition
self.action = action
@@ -309,7 +311,21 @@ class ForStatement(Statement):
"type": self.type().value,
"var_declaration": self.var_declaration.json(),
"condition": self.condition.json(),
"body": self.body.json()
"body": self.body.json(),
"action": self.action.json()
}
class DependStatement(Statement):
def __init__(self, file_path: str) -> None:
self.file_path = file_path
def type(self) -> NodeType:
return NodeType.DependStatement
def json(self) -> dict:
return {
"type": self.type().value,
"file_path": self.file_path
}
# endregion
@@ -360,4 +376,19 @@ class PrefixExpression(Expression):
"operator": self.operator,
"right_node": self.right_node.json()
}
class PostfixExpression(Expression):
def __init__(self, left_node: IdentifierLiteral, operator: str) -> None:
self.left_node = left_node
self.operator = operator
def type(self) -> NodeType:
return NodeType.PostfixExpression
def json(self) -> dict:
return {
"type": self.type().value,
"left_node": self.left_node.json(),
"operator": self.operator
}
# endregion

View File

@@ -1,14 +1,18 @@
from llvmlite import ir
import os
from AST import Node, NodeType, Program, Expression
from AST import ExpressionStatement, AssignmentStatement, BlockStatement, ReturnStatement, FunctionStatement, ReassignStatement, IfStatement
from AST import WhileStatement, BreakStatement, ContinueStatement, ForStatement
from AST import InfixExpression, CallExpression, PrefixExpression
from AST import WhileStatement, BreakStatement, ContinueStatement, ForStatement, DependStatement
from AST import InfixExpression, CallExpression, PrefixExpression, PostfixExpression
from AST import IntegerLiteral, FloatLiteral, IdentifierLiteral, BooleanLiteral, StringLiteral
from AST import FunctionParameter
from environment import Environment
from lexer import Lexer
from plasma_parser import Parser
class Compiler:
def __init__(self) -> None:
@@ -36,6 +40,8 @@ class Compiler:
self.breakpoints: list[ir.Block] = []
self.continues: list[ir.Block] = []
self.global_parsed_pallets: dict[str, Program] = {}
def __initialize_builtins(self) -> None:
def __init_print() -> ir.Function:
fnty: ir.FunctionType = ir.FunctionType(
@@ -96,12 +102,16 @@ class Compiler:
self.__visit_continue_statement(node)
case NodeType.ForStatement:
self.__visit_for_statement(node)
case NodeType.DependStatement:
self.__visit_depend_statement(node)
# Expressions
case NodeType.InfixExpression:
self.__visit_infix_expression(node)
case NodeType.CallExpression:
self.__visit_call_expression(node)
case NodeType.PostfixExpression:
self.__visit_postfix_expression(node)
# region Visit Methods
def __visit_program(self, node: Program) -> None:
@@ -326,6 +336,30 @@ class Compiler:
self.breakpoints.pop()
self.continues.pop()
def __visit_depend_statement(self, node: DependStatement) -> None:
file_path: str = node.file_path
if self.global_parsed_pallets.get(file_path) is not None:
print(f"warning: \"{file_path}\" is already imported globally!\n")
return
with open(os.path.abspath(file_path), "r") as f:
pallet_code: str = f.read()
l: Lexer = Lexer(pallet_code)
p: Parser = Parser(l)
program: Program = p.parse_program()
if len(p.errors) > 0:
print(f"Error in dependency: {file_path}")
for err in p.errors:
print(err)
exit(1)
self.compile(program)
self.global_parsed_pallets[file_path] = program
# endregion
# region Expressions
@@ -455,6 +489,32 @@ class Compiler:
value = self.builder.not_(right_value)
return value, Type
def __visit_postfix_expression(self, node: PostfixExpression) -> None:
left_node: IdentifierLiteral = node.left_node
operator: str = node.operator
if self.environment.lookup(left_node.value) is None:
self.errors.append(f"Identifier {left_node.value} has not been declared before it was re-assigned.")
return
var_ptr, _ = self.environment.lookup(left_node.value)
orig_value = self.builder.load(var_ptr)
value = None
match operator:
case "++":
if isinstance(orig_value.type, ir.IntType):
value = self.builder.add(orig_value, ir.Constant(ir.IntType(32), 1))
elif isinstance(orig_value.type, ir.FloatType):
value = self.builder.fadd(orig_value, ir.Constant(ir.FloatType(), 1.0))
case "--":
if isinstance(orig_value.type, ir.IntType):
value = self.builder.sub(orig_value, ir.Constant(ir.IntType(32), 1))
elif isinstance(orig_value.type, ir.FloatType):
value = self.builder.fsub(orig_value, ir.Constant(ir.FloatType(), 1.0))
self.builder.store(value, var_ptr)
# endregion
# endregion

47
debug/ast.json Normal file
View File

@@ -0,0 +1,47 @@
{
"type": "Program",
"statements": [
{
"DependStatement": {
"type": "DependStatement",
"file_path": "tests/math.pla"
}
},
{
"FunctionStatement": {
"type": "FunctionStatement",
"name": {
"type": "IdentifierLiteral",
"value": "main"
},
"return_type": "Int",
"parameters": [],
"body": {
"type": "BlockStatement",
"statements": [
{
"type": "ReturnStatement",
"return_value": {
"type": "CallExpression",
"function": {
"type": "IdentifierLiteral",
"value": "add"
},
"arguments": [
{
"type": "IntegerLiteral",
"value": 1
},
{
"type": "IntegerLiteral",
"value": 2
}
]
}
}
]
}
}
}
]
}

13
debug/ir.ll Normal file
View File

@@ -0,0 +1,13 @@
; ModuleID = "main"
target triple = "x86_64-unknown-linux-gnu"
target datalayout = ""
declare i32 @"printf"(i8* %".1", ...)
@"true" = constant i1 1
@"false" = constant i1 0
define i32 @"main"()
{
main_entry:
ret i32 0
}

View File

@@ -88,6 +88,10 @@ class Lexer:
ch = self.current_char
self.__read_char()
tok = self.__new_token(TokenType.PLUS_EQ, ch + self.current_char)
elif self.__peek_char() == "+":
ch = self.current_char
self.__read_char()
tok = self.__new_token(TokenType.PLUS_PLUS, ch + self.current_char)
else:
tok = self.__new_token(TokenType.PLUS, self.current_char)
case "-":
@@ -95,6 +99,10 @@ class Lexer:
ch = self.current_char
self.__read_char()
tok = self.__new_token(TokenType.MINUS_EQ, ch + self.current_char)
elif self.__peek_char() == "-":
ch = self.current_char
self.__read_char()
tok = self.__new_token(TokenType.MINUS_MINUS, ch + self.current_char)
else:
tok = self.__new_token(TokenType.MINUS, self.current_char)
case "*":

View File

@@ -47,8 +47,14 @@ class TokenType(Enum):
SEMICOLON = "SEMICOLON"
COMMA = "COMMA"
DOLLARSIGN = "DOLLARSIGN"
# Prefix symbols
BANG = "BANG"
# Postfix symbols
PLUS_PLUS = "PLUS_PLUS"
MINUS_MINUS = "MINUS_MINUS"
# Keywords
RETURN = "RETURN"
IF = "IF"
@@ -59,6 +65,7 @@ class TokenType(Enum):
CONTINUE = "CONTINUE"
BREAK = "BREAK"
FOR = "FOR"
DEPEND = "DEPEND"
# Typing
TYPE = "TYPE"
@@ -86,12 +93,12 @@ KEYWORDS: dict[str, TokenType] = {
"while": TokenType.WHILE,
"break": TokenType.BREAK,
"continue": TokenType.CONTINUE,
"for": TokenType.FOR
"for": TokenType.FOR,
"depend": TokenType.DEPEND
}
ALT_KEYWORDS: dict[str, TokenType] = {
"else": TokenType.UNLESS,
"ret": TokenType.RETURN
}
TYPE_KEYWORDS: list[str] = ["Int", "Float", "String", "Bool", "List", "Nil", "Func"]

View File

@@ -10,7 +10,7 @@ import llvmlite.binding as llvm
from ctypes import CFUNCTYPE, c_int, c_float
LEXER_DEBUG: bool = False
PARSER_DEBUG: bool = True
PARSER_DEBUG: bool = False
COMPILER_DEBUG: bool = False
RUN_CODE: bool = True

View File

@@ -5,8 +5,8 @@ from enum import Enum, auto
from AST import Statement, Expression, Program
from AST import ExpressionStatement, AssignmentStatement, FunctionStatement, ReturnStatement, BlockStatement, ReassignStatement, IfStatement, WhileStatement
from AST import InfixExpression, CallExpression, PrefixExpression
from AST import BreakStatement, ContinueStatement, ForStatement
from AST import InfixExpression, CallExpression, PrefixExpression, PostfixExpression
from AST import BreakStatement, ContinueStatement, ForStatement, DependStatement
from AST import IntegerLiteral, FloatLiteral, IdentifierLiteral, BooleanLiteral, StringLiteral
from AST import FunctionParameter
@@ -34,7 +34,10 @@ PRECEDENCES: dict[TokenType, PrecedenceType] = {
TokenType.GT: PrecedenceType.P_LESSGREATER,
TokenType.LT_EQ: PrecedenceType.P_LESSGREATER,
TokenType.GT_EQ: PrecedenceType.P_LESSGREATER,
TokenType.DOLLARSIGN: PrecedenceType.P_CALL
TokenType.DOLLARSIGN: PrecedenceType.P_CALL,
TokenType.PLUS_PLUS: PrecedenceType.P_INDEX,
TokenType.MINUS_MINUS: PrecedenceType.P_INDEX
}
class Parser:
@@ -76,6 +79,10 @@ class Parser:
TokenType.GT: self.__parse_infix_expression,
TokenType.LT_EQ: self.__parse_infix_expression,
TokenType.GT_EQ: self.__parse_infix_expression,
TokenType.PLUS_PLUS: self.__parse_postfix_expression,
TokenType.MINUS_MINUS: self.__parse_postfix_expression
}
@@ -158,6 +165,8 @@ class Parser:
return self.__parse_continue_statement()
case TokenType.FOR:
return self.__parse_for_statement()
case TokenType.DEPEND:
return self.__parse_depend_statement()
case _:
return self.__parse_expression_statement()
@@ -372,9 +381,7 @@ class Parser:
self.__next_token() # skip ;
stmt.action = self.__parse_assignment_statement()
stmt.action = self.__parse_expression(PrecedenceType.P_LOWEST)
self.__next_token()
@@ -384,6 +391,17 @@ class Parser:
stmt.body = self.__parse_block_statement()
return stmt
def __parse_depend_statement(self) -> DependStatement:
if not self.__expect_peek(TokenType.STRING):
return None
stmt: DependStatement = DependStatement(self.current_token.literal)
if not self.__expect_peek(TokenType.SEMICOLON):
return None
return stmt
# endregion
# region Expression Methods
@@ -393,9 +411,9 @@ class Parser:
self.__no_prefix_parse_function_error(self.current_token.type)
return None
left_expr: Expression = prefix_func()
while not self.__peek_token_is(TokenType.SEMICOLON) and precedence.value < self.__peek_precedence().value:
infix_func: Callable | None = self.infix_parse_functions.get(self.peek_token.type)
if infix_func is None:
return left_expr
@@ -403,6 +421,7 @@ class Parser:
self.__next_token()
left_expr = infix_func(left_expr)
return left_expr
@@ -457,6 +476,9 @@ class Parser:
prefix_expr.right_node = self.__parse_expression(PrecedenceType.P_PREFIX)
return prefix_expr
def __parse_postfix_expression(self, left_node: Expression) -> PostfixExpression:
return PostfixExpression(left_node, self.current_token.literal)
# endregion
# region Prefix Methods

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
main = Func(): Int {
$print("Hello, World!");
$print("Hello, World!\n");
return 0;
}

3
tests/math.pla Normal file
View File

@@ -0,0 +1,3 @@
add = Func(a: Int, b: Int): Int {
return a + b;
}

View File

@@ -1,7 +1,5 @@
depend "tests/math.pla";
main = Func(): Int {
a: Int = 10;
a += 1;
return a;
return $add(1 ,2);
}

View File

@@ -1,19 +0,0 @@
depend "io.pla"
enum Gender {
male,
female
}
struct Person {
name: String,
age: Int = 0,
speak: Func,
gender: Gender
}
speak = Func(sentence: String): Nil {
print(sentence)
}
max: Person = {"Max", 17, speak, Gender.male};

View File

@@ -1,16 +0,0 @@
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];
MY_CONSTANT: Const(String) = "foo bar";
print(String(myInt));
print(String(myDecimal));
print(String(myBoolean));
print(myString);
print(String(myList));