How to parse conditionals in Platzi?
Parsing is a fundamental part of parsing in any programming language and the Platzi programming language is no exception. To parse an if statement
, it is crucial to understand its structure. Let's see the importance of converting these elements into nodes within our abstract syntax tree (AST) and how to implement it in detail.
What should we consider in the structure of an if statement?
In the Platzi programming language, as in many others, an if statement
starts with a keyword yes
, followed by a condition. This condition is crucial since it will determine which code block, or sequence of statements, will be executed if true.
- Code Block: If the condition is true, the subsequent code block is executed. Code blocks are essentially sequences of statements that are executed only if the
if
is true.
- Alternative: Optionally, if the condition is false, an alternative code block can be executed. The possibility of not having an alternative makes the design flexible.
The power of conditionals in Platzi increases since they are considered expressions. This makes it possible, for example, to define a variable with an if
expression that functions as a ternary operator:
variable foo equals if x greater than y returns x else y.
How do we test the implementation?
To evaluate whether an if expression
is correctly implemented, specific tests are generated. These tests include:
- Tests without Alternates:
- Implement a test that validates an
if expression
without having an alternative block.
- Ensure that the first statement in the program is an
expression statement
that contains an if
expression.
Example Python code for such a test would be:
def test_if_expressions(): lexer = Lexer(...) parser = Parser(lexer) program = parser.parse_program() statement = program.statements[0] assert isinstance(statement, ExpressionStatement)
if_expr = statement.expression assert isinstance(if_expr, IfExpression) assert if_expr.condition is not None assert isinstance(if_expr.consequence, BlockStatement) assert len(if_expr.consequence.statements) == 1 assert if_expr.alternative is None
Node design and implementation: blocks and conditionals
To allow the proper parsing of conditionals, the corresponding classes must be designed:
- Block class: Represents a sequence of statements executed under a true condition. It includes methods to convert the sequence into a string.
class BlockStatement: def __init__(self, token, statements): self.token = token self.statements = statements
def to_string(self): out = [statement.to_string() for statement in self.statements] return ''.join(out)
- Class If (Conditional): An expression that contains a condition, a consequence, and potentially an alternative. The implementation of this class must allow for manageable syntax errors without compromising execution.
class IfExpression: def __init__(self, token, condition, consequence, alternative): self.token = token self.condition = condition self.consequence = consequence self.alternative = alternative
def to_string(self): out = f'if {self.condition.to_string()} {self .consequence.to_string()}' if self.alternative: out += f' else {self.alternative.to_string()}' return out
Both of these classes are fundamental to extending our parser's ability to understand and process conditionals correctly.
As you progress in understanding how conditionals and their structures work, it is vital to remember that robust test design is crucial to ensure that your parser handles expected use cases and potential syntax errors elegantly. Keep going, the learning path is full of exciting discoveries!
Want to see more contributions, questions and answers from the community?