started work on assignment operators
This commit is contained in:
20
AST.py
20
AST.py
@@ -21,6 +21,7 @@ class NodeType(Enum):
|
||||
# Expressions
|
||||
InfixExpression = "InfixExpression"
|
||||
CallExpression = "CallExpression"
|
||||
PrefixExpression = "PrefixExpression"
|
||||
|
||||
# Literals
|
||||
IntegerLiteral = "IntegerLiteral"
|
||||
@@ -221,8 +222,9 @@ class FunctionStatement(Statement):
|
||||
}
|
||||
|
||||
class ReassignStatement(Statement):
|
||||
def __init__(self, ident: Expression = None, right_value: Expression = None) -> None:
|
||||
def __init__(self, ident: Expression = None, operator: str = "", right_value: Expression = None) -> None:
|
||||
self.ident = ident
|
||||
self.operator = operator
|
||||
self.right_value = right_value
|
||||
|
||||
def type(self) -> NodeType:
|
||||
@@ -232,6 +234,7 @@ class ReassignStatement(Statement):
|
||||
return {
|
||||
"type": self.type().value,
|
||||
"ident": self.ident.json(),
|
||||
"operator": self.operator,
|
||||
"right_value": self.right_value.json()
|
||||
}
|
||||
|
||||
@@ -342,4 +345,19 @@ class CallExpression(Expression):
|
||||
"function": self.function.json(),
|
||||
"arguments": [arg.json() for arg in self.arguments]
|
||||
}
|
||||
|
||||
class PrefixExpression(Expression):
|
||||
def __init__(self, operator: str, right_node: Expression = None) -> None:
|
||||
self.operator = operator
|
||||
self.right_node = right_node
|
||||
|
||||
def type(self) -> NodeType:
|
||||
return NodeType.PrefixExpression
|
||||
|
||||
def json(self) -> dict:
|
||||
return {
|
||||
"type": self.type().value,
|
||||
"operator": self.operator,
|
||||
"right_node": self.right_node.json()
|
||||
}
|
||||
# endregion
|
||||
68
README.md
68
README.md
@@ -1,3 +1,71 @@
|
||||
# Plasma
|
||||
|
||||
The Plasma programming language.
|
||||
|
||||
# Syntax Guide
|
||||
## Functions
|
||||
All your logic must be contained in the "main" function, which looks like this:
|
||||
```cpp
|
||||
main = Func(): Int {
|
||||
|
||||
}
|
||||
```
|
||||
This is how you define functions. In this example, "main" is the name of the function, and `Int` is the return type. My function takes no arguments.
|
||||
If I wanted to make a function that takes arguments, it might look like this:
|
||||
```cpp
|
||||
add = Func(number1: Int, number2: Int): Int {
|
||||
return number1 + number2
|
||||
}
|
||||
```
|
||||
This is a function that adds two numbers together, here's the same function in Python:
|
||||
```py
|
||||
def add(number1: int, number2: int) -> int:
|
||||
return number1 + number2
|
||||
```
|
||||
|
||||
### Calling Functions
|
||||
To call a function, you type a dollar sign (the $ symbol), then the name of the function, and then you pass any of your arguments inbetween paranthesis, like this:
|
||||
```cpp
|
||||
add = Func(number1: Int, number2: Int): Int {
|
||||
return number1 + number2
|
||||
}
|
||||
...
|
||||
later in my code...
|
||||
...
|
||||
$add(9, 10);
|
||||
```
|
||||
|
||||
Here's a "Hello, World!" program in Plasma:
|
||||
```cpp
|
||||
main = Func(): Int {
|
||||
$print("Hello, World!\n");
|
||||
}
|
||||
```
|
||||
`print` is a builtin function, it's defined in every piece of code you write. It has the same formatting as the `printf` function in C, so if I wanted to print an integer I could do it like so:
|
||||
```cpp
|
||||
$print("%i\n", 123);
|
||||
```
|
||||
|
||||
|
||||
## Variables
|
||||
To create a variable, it's as simple as:
|
||||
```cpp
|
||||
name: Type = value;
|
||||
```
|
||||
|
||||
There are several types in Plasma, which are: `Int`, `Float`, `Bool`, `String`
|
||||
|
||||
Here's another example:
|
||||
```cpp
|
||||
name: String = "bob";
|
||||
age: Int = 23;
|
||||
```
|
||||
|
||||
To change the value of an already defined variable, you just have to provide the name, and then write an equals sign, like this:
|
||||
```cpp
|
||||
-- defining the variable
|
||||
age: Int = 23;
|
||||
|
||||
-- changing it later
|
||||
age = age + 1;
|
||||
```
|
||||
79
compiler.py
79
compiler.py
@@ -3,7 +3,7 @@ from llvmlite import ir
|
||||
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
|
||||
from AST import InfixExpression, CallExpression, PrefixExpression
|
||||
from AST import IntegerLiteral, FloatLiteral, IdentifierLiteral, BooleanLiteral, StringLiteral
|
||||
from AST import FunctionParameter
|
||||
|
||||
@@ -189,15 +189,56 @@ class Compiler:
|
||||
|
||||
def __visit_reassign_statement(self, node: ReassignStatement) -> None:
|
||||
name: str = node.ident.value
|
||||
operator: str = node.operator
|
||||
value: Expression = node.right_value
|
||||
|
||||
value, Type = self.__resolve_value(value)
|
||||
|
||||
|
||||
if self.environment.lookup(name) is None:
|
||||
self.errors.append(f"Identifier {name} has not been declared before it was re-assigned.")
|
||||
else:
|
||||
ptr, _ = self.environment.lookup(name)
|
||||
self.builder.store(value, ptr)
|
||||
return
|
||||
|
||||
right_value, right_type = self.__resolve_value(value)
|
||||
|
||||
var_ptr, _ = self.environment.lookup(name)
|
||||
orig_value = self.builder.load(var_ptr)
|
||||
|
||||
if isinstance(orig_value.type, ir.IntType) and isinstance(right_type, ir.FloatType):
|
||||
orig_value = self.builder.sitofp(orig_value, ir.FloatType())
|
||||
|
||||
if isinstance(orig_value.type, ir.FloatType) and isinstance(right_type, ir.IntType):
|
||||
right_value = self.builder.sitofp(right_value, ir.FloatType())
|
||||
|
||||
value = None
|
||||
Type = None
|
||||
match operator:
|
||||
case "=":
|
||||
value = right_value
|
||||
case "+=":
|
||||
if isinstance(orig_value.type, ir.IntType) and isinstance(right_type, ir.IntType):
|
||||
value = self.builder.add(orig_value, right_value)
|
||||
else:
|
||||
value = self.builder.fadd(orig_value, right_value)
|
||||
case "-=":
|
||||
if isinstance(orig_value.type, ir.IntType) and isinstance(right_type, ir.IntType):
|
||||
value = self.builder.sub(orig_value, right_value)
|
||||
else:
|
||||
value = self.builder.fsub(orig_value, right_value)
|
||||
case "*=":
|
||||
if isinstance(orig_value.type, ir.IntType) and isinstance(right_type, ir.IntType):
|
||||
value = self.builder.mul(orig_value, right_value)
|
||||
else:
|
||||
value = self.builder.fmul(orig_value, right_value)
|
||||
case "/=":
|
||||
if isinstance(orig_value.type, ir.IntType) and isinstance(right_type, ir.IntType):
|
||||
value = self.builder.sdiv(orig_value, right_value)
|
||||
else:
|
||||
value = self.builder.fdiv(orig_value, right_value)
|
||||
case _:
|
||||
print("Unsupported assignment operator.")
|
||||
|
||||
ptr, _ = self.environment.lookup(name)
|
||||
self.builder.store(value, ptr)
|
||||
|
||||
def __visit_if_statement(self, node: IfStatement) -> None:
|
||||
condition = node.condition
|
||||
@@ -388,6 +429,32 @@ class Compiler:
|
||||
ret = self.builder.call(func, args)
|
||||
|
||||
return ret, ret_type
|
||||
|
||||
def __visit_prefix_expression(self, node: PrefixExpression) -> tuple[ir.Value, ir.Type]:
|
||||
operator: str = node.operator
|
||||
right_node: Expression = node.right_node
|
||||
|
||||
right_value, right_type = self.__resolve_value(right_node)
|
||||
|
||||
Type = None
|
||||
value = None
|
||||
|
||||
if isinstance(right_type, ir.FloatType):
|
||||
Type = ir.FloatType
|
||||
match operator:
|
||||
case "-":
|
||||
value = self.builder.fmul(right_value, ir.Constant(ir.FloatType(), -1.0))
|
||||
case "!":
|
||||
value = ir.Constant(ir.IntType(1), 0)
|
||||
elif isinstance(right_type, ir.IntType):
|
||||
Type = ir.IntType(32)
|
||||
match operator:
|
||||
case "-":
|
||||
value = self.builder.mul(right_value, ir.Constant(ir.IntType(32), -1))
|
||||
case "!":
|
||||
value = self.builder.not_(right_value)
|
||||
|
||||
return value, Type
|
||||
# endregion
|
||||
|
||||
# endregion
|
||||
@@ -420,6 +487,8 @@ class Compiler:
|
||||
return self.__visit_infix_expression(node)
|
||||
case NodeType.CallExpression:
|
||||
return self.__visit_call_expression(node)
|
||||
case NodeType.PrefixExpression:
|
||||
return self.__visit_prefix_expression(node)
|
||||
|
||||
def __convert_string(self, string: str) -> tuple[ir.Constant, ir.ArrayType]:
|
||||
string = string.replace("\\n", "\n\0")
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
{
|
||||
"type": "Program",
|
||||
"statements": [
|
||||
{
|
||||
"FunctionStatement": {
|
||||
"type": "FunctionStatement",
|
||||
"name": {
|
||||
"type": "IdentifierLiteral",
|
||||
"value": "main"
|
||||
},
|
||||
"return_type": "Int",
|
||||
"parameters": [],
|
||||
"body": {
|
||||
"type": "BlockStatement",
|
||||
"statements": [
|
||||
{
|
||||
"type": "ForStatement",
|
||||
"var_declaration": {
|
||||
"type": "AssignmentStatement",
|
||||
"name": {
|
||||
"type": "IdentifierLiteral",
|
||||
"value": "x"
|
||||
},
|
||||
"value": {
|
||||
"type": "IntegerLiteral",
|
||||
"value": 1
|
||||
},
|
||||
"value_type": "Int"
|
||||
},
|
||||
"condition": {
|
||||
"type": "InfixExpression",
|
||||
"left_node": {
|
||||
"type": "IdentifierLiteral",
|
||||
"value": "x"
|
||||
},
|
||||
"operator": "<=",
|
||||
"right_node": {
|
||||
"type": "IntegerLiteral",
|
||||
"value": 20
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"type": "BlockStatement",
|
||||
"statements": [
|
||||
{
|
||||
"type": "ExpressionStatement",
|
||||
"expr": {
|
||||
"type": "CallExpression",
|
||||
"function": {
|
||||
"type": "IdentifierLiteral",
|
||||
"value": "print"
|
||||
},
|
||||
"arguments": [
|
||||
{
|
||||
"type": "StringLiteral",
|
||||
"value": "i = %i\\n"
|
||||
},
|
||||
{
|
||||
"type": "IdentifierLiteral",
|
||||
"value": "x"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "ReturnStatement",
|
||||
"return_value": {
|
||||
"type": "IdentifierLiteral",
|
||||
"value": "x"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
34
debug/ir.ll
34
debug/ir.ll
@@ -1,34 +0,0 @@
|
||||
; ModuleID = "main"
|
||||
target triple = "x86_64-pc-windows-msvc"
|
||||
target datalayout = ""
|
||||
|
||||
declare i32 @"printf"(i8* %".1", ...)
|
||||
|
||||
@"true" = constant i1 1
|
||||
@"false" = constant i1 0
|
||||
define i32 @"main"()
|
||||
{
|
||||
main_entry:
|
||||
%".2" = alloca i32
|
||||
store i32 0, i32* %".2"
|
||||
%".4" = load i32, i32* %".2"
|
||||
%".5" = icmp slt i32 %".4", 10
|
||||
br i1 %".5", label %"while_loop_entry_1", label %"while_loop_otherwise_1"
|
||||
while_loop_entry_1:
|
||||
%".7" = load i32, i32* %".2"
|
||||
%".8" = alloca [9 x i8]*
|
||||
store [9 x i8]* @"__str_2", [9 x i8]** %".8"
|
||||
%".10" = bitcast [9 x i8]* @"__str_2" to i8*
|
||||
%".11" = call i32 (i8*, ...) @"printf"(i8* %".10", i32 %".7")
|
||||
%".12" = load i32, i32* %".2"
|
||||
%".13" = add i32 %".12", 1
|
||||
store i32 %".13", i32* %".2"
|
||||
%".15" = load i32, i32* %".2"
|
||||
%".16" = icmp slt i32 %".15", 10
|
||||
br i1 %".16", label %"while_loop_entry_1", label %"while_loop_otherwise_1"
|
||||
while_loop_otherwise_1:
|
||||
%".18" = load i32, i32* %".2"
|
||||
ret i32 %".18"
|
||||
}
|
||||
|
||||
@"__str_2" = internal constant [9 x i8] c"a = %i\0a\00\00"
|
||||
31
lexer.py
31
lexer.py
@@ -84,13 +84,33 @@ class Lexer:
|
||||
|
||||
match self.current_char:
|
||||
case "+":
|
||||
tok = self.__new_token(TokenType.PLUS, self.current_char)
|
||||
if self.__peek_char() == "=":
|
||||
ch = self.current_char
|
||||
self.__read_char()
|
||||
tok = self.__new_token(TokenType.PLUS_EQ, ch + self.current_char)
|
||||
else:
|
||||
tok = self.__new_token(TokenType.PLUS, self.current_char)
|
||||
case "-":
|
||||
tok = self.__new_token(TokenType.MINUS, self.current_char)
|
||||
if self.__peek_char() == "=":
|
||||
ch = self.current_char
|
||||
self.__read_char()
|
||||
tok = self.__new_token(TokenType.MINUS_EQ, ch + self.current_char)
|
||||
else:
|
||||
tok = self.__new_token(TokenType.MINUS, self.current_char)
|
||||
case "*":
|
||||
tok = self.__new_token(TokenType.ASTERISK, self.current_char)
|
||||
if self.__peek_char() == "=":
|
||||
ch = self.current_char
|
||||
self.__read_char()
|
||||
tok = self.__new_token(TokenType.MUL_EQ, ch + self.current_char)
|
||||
else:
|
||||
tok = self.__new_token(TokenType.ASTERISK, self.current_char)
|
||||
case "/":
|
||||
tok = self.__new_token(TokenType.SLASH, self.current_char)
|
||||
if self.__peek_char() == "=":
|
||||
ch = self.current_char
|
||||
self.__read_char()
|
||||
tok = self.__new_token(TokenType.DIV_EQ, ch + self.current_char)
|
||||
else:
|
||||
tok = self.__new_token(TokenType.SLASH, self.current_char)
|
||||
case "^":
|
||||
tok = self.__new_token(TokenType.POW, self.current_char)
|
||||
case "%":
|
||||
@@ -126,8 +146,7 @@ class Lexer:
|
||||
self.__read_char()
|
||||
tok = self.__new_token(TokenType.NOT_EQ, ch + self.current_char)
|
||||
else:
|
||||
# TODO: handle BANG
|
||||
tok = self.__new_token(TokenType.ILLEGAL, self.current_char)
|
||||
tok = self.__new_token(TokenType.BANG, self.current_char)
|
||||
case "(":
|
||||
tok = self.__new_token(TokenType.LPAREN, self.current_char)
|
||||
case ")":
|
||||
|
||||
@@ -23,6 +23,10 @@ class TokenType(Enum):
|
||||
|
||||
# Assignment symbols
|
||||
EQ = "EQ"
|
||||
PLUS_EQ = "PLUS_EQ"
|
||||
MINUS_EQ = "MINUS_EQ"
|
||||
MUL_EQ = "MUL_EQ"
|
||||
DIV_EQ = "DIV_EQ"
|
||||
|
||||
# Comparison symbols
|
||||
LT = "<"
|
||||
@@ -43,6 +47,7 @@ class TokenType(Enum):
|
||||
SEMICOLON = "SEMICOLON"
|
||||
COMMA = "COMMA"
|
||||
DOLLARSIGN = "DOLLARSIGN"
|
||||
BANG = "BANG"
|
||||
|
||||
# Keywords
|
||||
RETURN = "RETURN"
|
||||
|
||||
2
main.py
2
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 = False
|
||||
PARSER_DEBUG: bool = True
|
||||
COMPILER_DEBUG: bool = False
|
||||
RUN_CODE: bool = True
|
||||
|
||||
|
||||
@@ -5,7 +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, BreakStatement, ContinueStatement, ForStatement
|
||||
from AST import InfixExpression, CallExpression, PrefixExpression
|
||||
from AST import BreakStatement, ContinueStatement, ForStatement
|
||||
from AST import IntegerLiteral, FloatLiteral, IdentifierLiteral, BooleanLiteral, StringLiteral
|
||||
from AST import FunctionParameter
|
||||
|
||||
@@ -57,7 +58,10 @@ class Parser:
|
||||
|
||||
TokenType.STRING: self.__parse_string_literal,
|
||||
|
||||
TokenType.DOLLARSIGN: self.__parse_call_expression
|
||||
TokenType.DOLLARSIGN: self.__parse_call_expression,
|
||||
|
||||
TokenType.MINUS: self.__parse_prefix_expression,
|
||||
TokenType.BANG: self.__parse_prefix_expression,
|
||||
}
|
||||
self.infix_parse_functions: dict[Token, Callable] = { # 5 + 5
|
||||
TokenType.PLUS: self.__parse_infix_expression,
|
||||
@@ -89,6 +93,16 @@ class Parser:
|
||||
def __peek_token_is(self, tt: TokenType) -> bool:
|
||||
return self.peek_token.type == tt
|
||||
|
||||
def __peek_token_is_assignment(self) -> bool:
|
||||
assignment_operators: list[TokenType] = [
|
||||
TokenType.EQ,
|
||||
TokenType.PLUS_EQ,
|
||||
TokenType.MINUS_EQ,
|
||||
TokenType.MUL_EQ,
|
||||
TokenType.DIV_EQ,
|
||||
]
|
||||
return self.peek_token.type in assignment_operators
|
||||
|
||||
def __expect_peek(self, tt: TokenType) -> bool:
|
||||
if self.__peek_token_is(tt):
|
||||
self.__next_token()
|
||||
@@ -161,12 +175,13 @@ class Parser:
|
||||
# x: Int = 10;
|
||||
stmt: AssignmentStatement = AssignmentStatement(name=IdentifierLiteral(self.current_token.literal))
|
||||
|
||||
if self.__peek_token_is(TokenType.EQ): # function definition
|
||||
if self.__peek_token_is_assignment(): # function definition
|
||||
# x = Func(): Int { return 10; }
|
||||
|
||||
|
||||
self.__next_token()
|
||||
|
||||
if self.__peek_token_is(TokenType.TYPE):
|
||||
if self.__current_token_is(TokenType.EQ) and self.__peek_token_is(TokenType.TYPE):
|
||||
func_stmt: FunctionStatement = FunctionStatement(name=stmt.name)
|
||||
|
||||
if self.peek_token.literal != "Func":
|
||||
@@ -197,9 +212,12 @@ class Parser:
|
||||
else: # reassignment statement
|
||||
assign_stmt: ReassignStatement = ReassignStatement()
|
||||
|
||||
|
||||
assign_stmt.operator = self.current_token.literal
|
||||
assign_stmt.ident = stmt.name
|
||||
|
||||
self.__next_token()
|
||||
|
||||
assign_stmt.ident = stmt.name
|
||||
assign_stmt.right_value = self.__parse_expression(PrecedenceType.P_LOWEST)
|
||||
|
||||
|
||||
@@ -430,6 +448,15 @@ class Parser:
|
||||
return None
|
||||
|
||||
return e_list
|
||||
|
||||
def __parse_prefix_expression(self) -> PrefixExpression:
|
||||
prefix_expr: PrefixExpression = PrefixExpression(operator=self.current_token.literal)
|
||||
|
||||
self.__next_token()
|
||||
|
||||
prefix_expr.right_node = self.__parse_expression(PrecedenceType.P_PREFIX)
|
||||
|
||||
return prefix_expr
|
||||
# endregion
|
||||
|
||||
# region Prefix Methods
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
main = Func(): Int {
|
||||
for (x: Int = 1; x <= 20; x = x + 1;) {
|
||||
$print("i = %i\n", x)
|
||||
}
|
||||
a: Int = 10;
|
||||
|
||||
return x;
|
||||
a += 1;
|
||||
|
||||
return a;
|
||||
}
|
||||
Reference in New Issue
Block a user