Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unary Operators #2

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PYTHONPATH=bagel
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__pycache__/**
*.pyc
.pytest_cache/**
15 changes: 15 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Module",
"type": "python",
"request": "launch",
"module": "bagel",
"justMyCode": true
}
]
}
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"python.linting.enabled": false
}
15 changes: 15 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "bagel: REPL",
"type": "shell",
"command": "python -m bagel",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
2 changes: 1 addition & 1 deletion bagel/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.1.0'
__version__ = '0.1.5'
48 changes: 15 additions & 33 deletions bagel/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
import colorama
from colorama import Fore

from .codeanalysis.binding.binder import Binder
from .codeanalysis.evaluator import Evaluator
from .codeanalysis.syntaxnode import SyntaxNode
from .codeanalysis.syntaxtoken import SyntaxToken
from .codeanalysis.syntaxtree import SyntaxTree
from .codeanalysis.syntax.syntaxnode import SyntaxNode
from .codeanalysis.syntax.syntaxtoken import SyntaxToken
from .codeanalysis.syntax.syntaxtree import SyntaxTree

colorama.init(autoreset=True)
show_tree = False
SHOW_TREE = False


def pretty_print(node: SyntaxNode, indent: str = "", is_last: bool = True):
Expand Down Expand Up @@ -40,49 +41,30 @@ def pretty_print(node: SyntaxNode, indent: str = "", is_last: bool = True):
while True:
line = input("» ")

# ideas
# a = input("» ")
# a = input("› ")
# a = input("¶ ")
# a = input("~ ")
# a = input("⇝ ")
# a = input("⇢ ")
# a = input("⇻ ")
# a = input("⇾ ")
# a = input("∢ ")
# a = input("∝ ")
# a = input("⊱ ")

# a = input("⊶ ")
# a = input("⊷ ")
# a = input("⊸ ")

# a = input("⋉ ")
# a = input("⋯ ")
# a = input("⨊ ")
# a = input("⨭ ")
# a = input("⫻ ")

if line is None or line == "":
break

if line == "#showtree":
show_tree = not show_tree
print("Showing parser trees" if show_tree else "Not showing parser trees")
SHOW_TREE = not SHOW_TREE
print("Showing parser trees" if SHOW_TREE else "Not showing parser trees")
continue
elif line == "#cls":
os.system('cls')
continue

syntax_tree = SyntaxTree.parse(line)
binder = Binder()
bound_expression = binder.bind_expression(syntax_tree.root)

diagnostics = syntax_tree.diagnostics + binder.diagnostics

if show_tree:
if SHOW_TREE:
pretty_print(syntax_tree.root)

if not len(syntax_tree.diagnostics) > 0:
evaluator = Evaluator(syntax_tree.root)
if not any(syntax_tree.diagnostics):
evaluator = Evaluator(bound_expression)
result = evaluator.evaluate()
print(str(result))
print(result)
else:
for _diagnostic in syntax_tree.diagnostics:
print(Fore.RED + _diagnostic)
Binary file removed bagel/__pycache__/__init__.cpython-310.pyc
Binary file not shown.
Binary file removed bagel/__pycache__/__main__.cpython-310.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed bagel/codeanalysis/__pycache__/lexer.cpython-310.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
9 changes: 9 additions & 0 deletions bagel/codeanalysis/binding/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from .boundnode import BoundNode
from .boundnodekind import BoundNodeKind
from .boundexpression import BoundExpression
from .boundliteralexpression import BoundLiteralExpression
from .boundunaryoperatorkind import BoundUnaryOperatorKind
from .boundunaryexpression import BoundUnaryExpression
from .boundbinaryoperatorkind import BoundBinaryOperatorKind
from .boundbinaryexpression import BoundBinaryExpression
from .binder import Binder
88 changes: 88 additions & 0 deletions bagel/codeanalysis/binding/binder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from typing import List

from ..syntax.binary_expression_syntax import BinaryExpressionSyntax
from ..syntax.expression_syntax import ExpressionSyntax
from ..syntax.literal_expression_syntax import LiteralExpressionSyntax
from ..syntax.syntaxkind import SyntaxKind
from ..syntax.unary_expression_syntax import UnaryExpressionSyntax

from .boundexpression import BoundExpression
from .boundbinaryexpression import BoundBinaryExpression
from .boundunaryexpression import BoundUnaryExpression
from .boundliteralexpression import BoundLiteralExpression
from .boundunaryoperatorkind import BoundUnaryOperatorKind
from .boundbinaryoperatorkind import BoundBinaryOperatorKind


class Binder:
_diagnostics: List[str]

def __init__(self):
self._diagnostics = []

@property
def diagnostics(self) -> List[str]:
return self._diagnostics

def bind_expression(self, syntax: ExpressionSyntax) -> BoundExpression:
match syntax.kind:
case SyntaxKind.LITERALEXPRESSION:
return self.bind_literal_expression(syntax)
case SyntaxKind.UNARYEXPRESSION:
return self.bind_unary_expression(syntax)
case SyntaxKind.BINARYEXPRESSION:
return self.bind_binary_expression(syntax)
case _:
raise Exception(f"Unexpected syntax {syntax.kind}")

def bind_literal_expression(self, syntax: LiteralExpressionSyntax) -> BoundLiteralExpression:
value = syntax.value if syntax.value != None else 0
return BoundLiteralExpression(value)

def bind_unary_expression(self, syntax: UnaryExpressionSyntax) -> BoundUnaryExpression:
bound_operand = self.bind_expression(syntax.operand)
bound_operator_kind = self.bind_unary_operator_kind(syntax.operator_token.kind, bound_operand.type)

if bound_operator_kind is None:
self._diagnostics.append(f"Unary operator {syntax.operator_token.text} is not defined for type {bound_operand.type}.")
return bound_operand

return BoundUnaryExpression(bound_operator_kind, bound_operand)

def bind_binary_expression(self, syntax: BinaryExpressionSyntax) -> BoundBinaryExpression:
bound_left = self.bind_expression(syntax.left)
bound_right = self.bind_expression(syntax.right)
bound_operator_kind = self.bind_binary_operator_kind(syntax.operator_token.kind, bound_left.type, bound_right.type)
if bound_operator_kind is None:
self._diagnostics.append(f"Binary operator {syntax.operator_token.text} is not defined for types {bound_left.type} and {bound_right.type}.")
return bound_left

return BoundBinaryExpression(bound_left, bound_operator_kind, bound_right)

def bind_unary_operator_kind(self, kind: SyntaxKind, operand_type: int) -> BoundUnaryOperatorKind | None:
if operand_type != int:
return

match kind:
case SyntaxKind.PLUSTOKEN:
return BoundUnaryOperatorKind.IDENTITY
case SyntaxKind.MINUSTOKEN:
return BoundUnaryOperatorKind.NEGATION
case _:
raise Exception(f"Unexpected unary operator kind {kind}")

def bind_binary_operator_kind(self, kind: SyntaxKind, left_type: int, right_type: int) -> BoundBinaryOperatorKind | None:
if left_type != int or right_type != int:
return

match kind:
case SyntaxKind.PLUSTOKEN:
return BoundBinaryOperatorKind.ADDITION
case SyntaxKind.MINUSTOKEN:
return BoundBinaryOperatorKind.SUBTRACTION
case SyntaxKind.STARTOKEN:
return BoundBinaryOperatorKind.MULTIPLICATION
case SyntaxKind.SLASHTOKEN:
return BoundBinaryOperatorKind.DIVISION
case _:
raise Exception(f"Unexpected binary operator kind {kind}")
34 changes: 34 additions & 0 deletions bagel/codeanalysis/binding/boundbinaryexpression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from .boundexpression import BoundExpression
from .boundnodekind import BoundNodeKind
from .boundunaryoperatorkind import BoundUnaryOperatorKind
from .boundbinaryoperatorkind import BoundBinaryOperatorKind

class BoundBinaryExpression(BoundExpression):
_left: BoundExpression
_operator_kind: BoundUnaryOperatorKind
_right: BoundExpression

def __init__(self, left: BoundExpression, operator_kind: BoundBinaryOperatorKind, right: BoundExpression):
self._left = left
self._operator_kind = operator_kind
self._right = right

@property
def kind(self) -> BoundNodeKind:
return BoundNodeKind.BINARYEXPRESSION

@property
def type(self):
return self._left.type

@property
def left(self) -> BoundExpression:
return self._left

@property
def operator_kind(self) -> BoundUnaryOperatorKind:
return self._operator_kind

@property
def right(self) -> BoundExpression:
return self._right
8 changes: 8 additions & 0 deletions bagel/codeanalysis/binding/boundbinaryoperatorkind.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from enum import Enum


class BoundBinaryOperatorKind(Enum):
ADDITION = 0
SUBTRACTION = 1
MULTIPLICATION = 2
DIVISION = 3
9 changes: 9 additions & 0 deletions bagel/codeanalysis/binding/boundexpression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from .boundnode import BoundNode


class BoundExpression(BoundNode):
_type: object

@property
def type(self):
return self._type
21 changes: 21 additions & 0 deletions bagel/codeanalysis/binding/boundliteralexpression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from .boundexpression import BoundExpression
from .boundnodekind import BoundNodeKind


class BoundLiteralExpression(BoundExpression):
_value: object

def __init__(self, value: object):
self._value = value

@property
def kind(self) -> BoundNodeKind:
return BoundNodeKind.LITERALEXPRESSION

@property
def type(self):
return type(self._value)

@property
def value(self):
return self._value
9 changes: 9 additions & 0 deletions bagel/codeanalysis/binding/boundnode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from .boundnodekind import BoundNodeKind


class BoundNode:
_kind: BoundNodeKind

@property
def kind(self) -> BoundNodeKind:
return self._kind
7 changes: 7 additions & 0 deletions bagel/codeanalysis/binding/boundnodekind.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from enum import Enum


class BoundNodeKind(Enum):
LITERALEXPRESSION = 0
UNARYEXPRESSION = 1
BINARYEXPRESSION = 2
27 changes: 27 additions & 0 deletions bagel/codeanalysis/binding/boundunaryexpression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from .boundnodekind import BoundNodeKind
from .boundunaryoperatorkind import BoundUnaryOperatorKind
from .boundexpression import BoundExpression

class BoundUnaryExpression(BoundExpression):
_operator_kind: BoundUnaryOperatorKind
_operand: BoundExpression

def __init__(self, operator_kind: BoundUnaryOperatorKind, operand: BoundExpression):
self._operator_kind = operator_kind
self._operand = operand

@property
def kind(self) -> BoundNodeKind:
return BoundNodeKind.UNARYEXPRESSION

@property
def type(self):
return self._operand.type

@property
def operator_kind(self) -> BoundUnaryOperatorKind:
return self._operator_kind

@property
def operand(self) -> BoundExpression:
return self._operand
6 changes: 6 additions & 0 deletions bagel/codeanalysis/binding/boundunaryoperatorkind.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from enum import Enum


class BoundUnaryOperatorKind(Enum):
IDENTITY = 0
NEGATION = 1
Loading