diff --git a/AST.py b/AST.py index 94a0084..742559e 100644 --- a/AST.py +++ b/AST.py @@ -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 \ No newline at end of file diff --git a/compiler.py b/compiler.py index 9086bcd..0b82cc2 100644 --- a/compiler.py +++ b/compiler.py @@ -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 diff --git a/debug/ast.json b/debug/ast.json new file mode 100644 index 0000000..b86a162 --- /dev/null +++ b/debug/ast.json @@ -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 + } + ] + } + } + ] + } + } + } + ] +} \ No newline at end of file diff --git a/debug/ir.ll b/debug/ir.ll new file mode 100644 index 0000000..6cc09e9 --- /dev/null +++ b/debug/ir.ll @@ -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 +} diff --git a/lexer.py b/lexer.py index 0c2ba95..eaa31b7 100644 --- a/lexer.py +++ b/lexer.py @@ -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 "*": diff --git a/lexer_token.py b/lexer_token.py index 5c62976..8d6eabd 100644 --- a/lexer_token.py +++ b/lexer_token.py @@ -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"] diff --git a/main.py b/main.py index 1b1791c..aaec4f1 100644 --- a/main.py +++ b/main.py @@ -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 diff --git a/plasma_parser.py b/plasma_parser.py index d912ed4..43ff7c2 100644 --- a/plasma_parser.py +++ b/plasma_parser.py @@ -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 diff --git a/tests/conditionals.pla b/tests/conditionals.pla deleted file mode 100644 index b894b5a..0000000 --- a/tests/conditionals.pla +++ /dev/null @@ -1,9 +0,0 @@ -depend "io.pla" - -if 1 + 2 == 3 { - print("The universe is functional!"); -} -unless -{ - print("WHAT, HOW"); -} \ No newline at end of file diff --git a/tests/functions.pla b/tests/functions.pla deleted file mode 100644 index c520992..0000000 --- a/tests/functions.pla +++ /dev/null @@ -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))); \ No newline at end of file diff --git a/tests/helloWorld.pla b/tests/helloWorld.pla index 3ffcb1c..fde5807 100644 --- a/tests/helloWorld.pla +++ b/tests/helloWorld.pla @@ -1,4 +1,4 @@ main = Func(): Int { - $print("Hello, World!"); + $print("Hello, World!\n"); return 0; } \ No newline at end of file diff --git a/tests/math.pla b/tests/math.pla new file mode 100644 index 0000000..1f4ad01 --- /dev/null +++ b/tests/math.pla @@ -0,0 +1,3 @@ +add = Func(a: Int, b: Int): Int { + return a + b; +} \ No newline at end of file diff --git a/tests/test.pla b/tests/test.pla index 584041b..2d7aac9 100644 --- a/tests/test.pla +++ b/tests/test.pla @@ -1,7 +1,5 @@ +depend "tests/math.pla"; + main = Func(): Int { - a: Int = 10; - - a += 1; - - return a; + return $add(1 ,2); } \ No newline at end of file diff --git a/tests/types.pla b/tests/types.pla deleted file mode 100644 index 6e878a2..0000000 --- a/tests/types.pla +++ /dev/null @@ -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}; \ No newline at end of file diff --git a/tests/variables.pla b/tests/variables.pla deleted file mode 100644 index a51887d..0000000 --- a/tests/variables.pla +++ /dev/null @@ -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)); \ No newline at end of file