Skip to content
This repository has been archived by the owner on Jul 3, 2022. It is now read-only.

Commit

Permalink
Finish chapter 5: Add ast_printer and test thr created functions
Browse files Browse the repository at this point in the history
  • Loading branch information
RoelAdriaans committed Aug 6, 2020
1 parent 4c6913c commit 06320e0
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 5 deletions.
11 changes: 8 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.0.2] - 2020-08-06

### Added

- Completing chapter 5
- Completed including chapter 5.3.3
- Created `tools/generate_ast.py`, added Visitor class
- Added the generated `expr.py`
- Not implemented methods in ABC base classes will raise an `NotImplementedError` that
is ignored by coverage tests

### Fixed

- CTRL-D now works on OSX terminal
- CTRL-D now works in the OSX terminal
- Refactor: Exit method is called in a single place

## [0.0.1] - 2020-08-01
Expand All @@ -30,5 +34,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `Token`, `Scanner`, `TokenType`
- Support `strings`, `numbers` and `identifiers`

[Unreleased]: https://github.com/RoelAdriaans/yaplox/compare/v0.0.1...HEAD
[Unreleased]: https://github.com/RoelAdriaans/yaplox/compare/v0.0.2...HEAD
[0.0.2]: https://github.com/RoelAdriaans/yaplox/releases/tag/v0.0.2
[0.0.1]: https://github.com/RoelAdriaans/yaplox/releases/tag/v0.0.1
31 changes: 31 additions & 0 deletions src/yaplox/ast_printer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from yaplox.expr import Binary, Expr, ExprVisitor, Grouping, Literal, Unary


class AstPrinter(ExprVisitor):
def print(self, expr: Expr) -> str:
return expr.accept(self)

def visit_grouping_expr(self, expr: Grouping):
return self._parenthesize("group", expr.expression)

def visit_literal_expr(self, expr: Literal):
if expr.value is None:
return "nil"
return str(expr.value)

def visit_unary_expr(self, expr: Unary):
return self._parenthesize(expr.operator.lexeme, expr.right)

def visit_binary_expr(self, expr: Binary):
return self._parenthesize(expr.operator.lexeme, expr.left, expr.right)

def _parenthesize(self, name: str, *args: Expr):
expressions = ["(", name]

for expr in args:
expressions.append(" ")
expressions.append(expr.accept(self))

expressions.append(")")

return "".join(expressions)
72 changes: 72 additions & 0 deletions src/yaplox/expr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# This file has been auto-generated by tools/generate_ast.py
# Do not edit this file by hand. Or do, but it will be overwritten

from abc import ABC, abstractmethod
from typing import Any

from yaplox.token import Token


class ExprVisitor(ABC):
"""This class is used as an Vistor for the Expr class"""

@abstractmethod
def visit_binary_expr(self, expr: "Binary"):
raise NotImplementedError

@abstractmethod
def visit_grouping_expr(self, expr: "Grouping"):
raise NotImplementedError

@abstractmethod
def visit_literal_expr(self, expr: "Literal"):
raise NotImplementedError

@abstractmethod
def visit_unary_expr(self, expr: "Unary"):
raise NotImplementedError


class Expr(ABC):
@abstractmethod
def accept(self, visitor: ExprVisitor):
raise NotImplementedError


class Binary(Expr):
def __init__(self, left: Expr, operator: Token, right: Expr):
self.left = left
self.operator = operator
self.right = right

def accept(self, visitor: ExprVisitor):
""" Create a accept method that calls the visitor. """
return visitor.visit_binary_expr(self)


class Grouping(Expr):
def __init__(self, expression: Expr):
self.expression = expression

def accept(self, visitor: ExprVisitor):
""" Create a accept method that calls the visitor. """
return visitor.visit_grouping_expr(self)


class Literal(Expr):
def __init__(self, value: Any):
self.value = value

def accept(self, visitor: ExprVisitor):
""" Create a accept method that calls the visitor. """
return visitor.visit_literal_expr(self)


class Unary(Expr):
def __init__(self, operator: Token, right: Expr):
self.operator = operator
self.right = right

def accept(self, visitor: ExprVisitor):
""" Create a accept method that calls the visitor. """
return visitor.visit_unary_expr(self)
27 changes: 27 additions & 0 deletions tests/test_ast_printer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from yaplox import expr
from yaplox.ast_printer import AstPrinter
from yaplox.token import Token
from yaplox.token_type import TokenType


class TestAstPrinter:
def test_astrinter(self):
# This is the main/test function given in the chapter for the
# ast_printer in chapter A (Not Very) pretty printer.
# This code might be obsolete in the future, but it is a lot cleaner then
# creating a main function to test our code.
expression = expr.Binary(
expr.Unary(Token(TokenType.MINUS, "-", None, 1), expr.Literal(123)),
Token(TokenType.STAR, "*", None, 1),
expr.Grouping(expr.Literal(45.67)),
)

result = AstPrinter().print(expression)
assert result == "(* (- 123) (group 45.67))"

def test_astrinter_nill(self):
# Test a single edge case None
expression = expr.Literal(None)

result = AstPrinter().print(expression)
assert result == "nil"
11 changes: 9 additions & 2 deletions tools/generate_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,14 @@ def _define_ast(self, base_name: str, types: List):
]
lines.extend(self._define_visitor(base_name, types))

lines.extend([f"class {base_name}(ABC):", " ...", ""])
lines.extend(
[
f"class {base_name}(ABC):",
" @abstractmethod",
" def accept(self, visitor: ExprVisitor):",
" raise NotImplementedError",
]
)

for class_type in types:
class_name = class_type.split(":")[0].strip()
Expand All @@ -73,7 +80,7 @@ def _define_visitor(self, base_name: str, types: List) -> List:
" @abstractmethod",
f" def visit_{class_name.lower()}_{base_name.lower()}"
f'(self, expr: "{class_name}"):',
" pass",
" raise NotImplementedError",
]
vistor_lines.extend(lines)

Expand Down

0 comments on commit 06320e0

Please sign in to comment.