Skip to content

Commit

Permalink
Booleans and Grouped Expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
madsravn committed Oct 23, 2023
1 parent 7027cf4 commit 2866846
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 17 deletions.
29 changes: 29 additions & 0 deletions src/main/java/dk/madsravn/interpreter/ast/BooleanType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package dk.madsravn.interpreter.ast;

import dk.madsravn.interpreter.tokens.Token;

public class BooleanType implements IExpression {
private Token token;
private boolean value;

public BooleanType(Token token, boolean value) {
this.token = token;
this.value = value;
}
@Override
public void expressionNode() {}

@Override
public String tokenLiteral() {
return token.getLiteral();
}

public boolean getValue() {
return value;
}

@Override
public String string() {
return token.getLiteral();
}
}
12 changes: 12 additions & 0 deletions src/main/java/dk/madsravn/interpreter/ast/InfixExpression.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ public InfixExpression(Token token, IExpression left, String operator, IExpressi
this.right = right;
}

public String getOperator(){
return operator;
}

public IExpression getRight() {
return right;
}

public IExpression getLeft() {
return left;
}

@Override
public void expressionNode() {}

Expand Down
20 changes: 17 additions & 3 deletions src/main/java/dk/madsravn/interpreter/parser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,26 @@ private IExpression getPrefixExpression() {
return parseIntegerLiteral();
case BANG, MINUS:
return parsePrefixExpression();
case TRUE, FALSE:
return parseBoolean();
case LPAREN:
return parseGroupedExpression();
default:
// TODO: Add tests for these
//noPrefixParseFunctionError(currentToken.getType());
noPrefixParseFunctionError(currentToken.getType());
// TODO: This is ugly
return null;
}
}

private IExpression parseGroupedExpression() {
nextToken();
IExpression expression = parseExpression(LOWEST);
if (!expectPeekType(RPAREN)) {
return null;
}
return expression;
}

private IExpression parseExpression(PrecedenceEnum precedence) {
IExpression left = getPrefixExpression();
if (left == null) {
Expand Down Expand Up @@ -148,7 +160,9 @@ private IExpression parseInfixExpression(IExpression left) {
return new InfixExpression(token, left, token.getLiteral(), right);
}


private BooleanType parseBoolean() {
return new BooleanType(currentToken, currentTokenType(TokenType.TRUE));
}

private void noPrefixParseFunctionError(TokenType tokenType) {
errors.add("No prefix parse function found for " + tokenType);
Expand Down
191 changes: 177 additions & 14 deletions src/test/java/dk/madsravn/interpreter/parser/ParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public void TestLetStatements() {
""";
Lexer lexer = new Lexer(input);
Parser parser = new Parser(lexer);
checkForParseErrors(parser);
checkForParseErrors(parser, input);

Program program = parser.parseProgram();
assertNotNull(program, "program is null");
Expand Down Expand Up @@ -47,7 +47,7 @@ public void TestReturnStatements() {
""";
Lexer lexer = new Lexer(input);
Parser parser = new Parser(lexer);
checkForParseErrors(parser);
checkForParseErrors(parser, input);


Program program = parser.parseProgram();
Expand All @@ -71,7 +71,7 @@ public void TestLetStatementWithErrors() {

// TODO: Should the errors be on the program or on the parser?
Program program = parser.parseProgram();
assertEquals(parser.getErrors().size(), 3);
assertEquals(parser.getErrors().size(), 4);
}

@Test
Expand Down Expand Up @@ -114,7 +114,7 @@ public void testIntegerLiteralExpression() {
Lexer lexer = new Lexer(input);
Parser parser = new Parser(lexer);
Program program = parser.parseProgram();
checkForParseErrors(parser);
checkForParseErrors(parser, input);

assertEquals(program.getStatementsLength(), 1);
IStatement statement = program.getStatements().get(0);
Expand All @@ -127,25 +127,178 @@ public void testIntegerLiteralExpression() {
assertEquals(integerLiteral.tokenLiteral(), "5");
}

private class PrefixData {


// TODO: InfixDataBoolean and InfixDataInteger needs to be merged somehow
private class InfixDataBoolean {
public String input;
public String operator;
public boolean leftValue;
public boolean rightValue;
public InfixDataBoolean(String input, boolean leftValue, String operator, boolean rightValue) {
this.input = input;
this.leftValue = leftValue;
this.operator = operator;
this.rightValue = rightValue;
}
}

@Test
public void testParsingBooleanInfixExpressions() {
List<InfixDataBoolean> inputs = Arrays.asList(
new InfixDataBoolean("true == true;", true, "==", true),
new InfixDataBoolean("true == false;", true, "==", false),
new InfixDataBoolean("false == true;", false, "==", true),
new InfixDataBoolean("false == false;", false, "==", false),
new InfixDataBoolean("true != true;", true, "!=", true),
new InfixDataBoolean("true != false;", true, "!=", false),
new InfixDataBoolean("false != true;", false, "!=", true),
new InfixDataBoolean("false != false;", false, "!=", false)
);
for(InfixDataBoolean infixData : inputs) {
Lexer lexer = new Lexer(infixData.input);
Parser parser = new Parser(lexer);
Program program = parser.parseProgram();
checkForParseErrors(parser, infixData.input);

assertEquals(program.getStatementsLength(), 1);
assertTrue(program.getStatements().get(0) instanceof ExpressionStatement);
ExpressionStatement statement = (ExpressionStatement) program.getStatements().get(0);
assertTrue(statement.getExpression() instanceof InfixExpression);
InfixExpression infixExpression = (InfixExpression) statement.getExpression();
assertEquals(infixExpression.getOperator(), infixData.operator);

// TODO: testIntegerLiteral method for later
assertTrue(infixExpression.getRight() instanceof BooleanType);
BooleanType rightBooleanLiteral = (BooleanType) infixExpression.getRight();

assertEquals(rightBooleanLiteral.getValue(), infixData.rightValue);
assertEquals(rightBooleanLiteral.tokenLiteral(), "" + infixData.rightValue);

assertTrue(infixExpression.getLeft() instanceof BooleanType);
BooleanType leftBooleanLiteral = (BooleanType) infixExpression.getLeft();

assertEquals(leftBooleanLiteral.getValue(), infixData.leftValue);
assertEquals(leftBooleanLiteral.tokenLiteral(), "" + infixData.leftValue);
}

}

private class InfixDataInteger {
public String input;
public String operator;
public int leftValue;
public int rightValue;
public InfixDataInteger(String input, int leftValue, String operator, int rightValue) {
this.input = input;
this.leftValue = leftValue;
this.operator = operator;
this.rightValue = rightValue;
}
}

@Test
public void testParsingIntegerInfixExpressions() {
List<InfixDataInteger> inputs = Arrays.asList(
new InfixDataInteger("5 + 5;", 5, "+", 5),
new InfixDataInteger("5 - 5;", 5, "-", 5),
new InfixDataInteger("5 * 5;", 5, "*", 5),
new InfixDataInteger("5 / 5;", 5, "/", 5),
new InfixDataInteger("5 > 5;", 5, ">", 5),
new InfixDataInteger("5 < 5;", 5, "<", 5),
new InfixDataInteger("5 == 5;", 5, "==", 5),
new InfixDataInteger("5 != 5;", 5, "!=", 5)
);
for(InfixDataInteger infixData : inputs) {
Lexer lexer = new Lexer(infixData.input);
Parser parser = new Parser(lexer);
Program program = parser.parseProgram();
checkForParseErrors(parser, infixData.input);

assertEquals(program.getStatementsLength(), 1);
assertTrue(program.getStatements().get(0) instanceof ExpressionStatement);
ExpressionStatement statement = (ExpressionStatement) program.getStatements().get(0);
assertTrue(statement.getExpression() instanceof InfixExpression);
InfixExpression infixExpression = (InfixExpression) statement.getExpression();
assertEquals(infixExpression.getOperator(), infixData.operator);

// TODO: testIntegerLiteral method for later
assertTrue(infixExpression.getRight() instanceof IntegerLiteral);
IntegerLiteral rightIntegerLiteral = (IntegerLiteral) infixExpression.getRight();

assertEquals(rightIntegerLiteral.getValue(), infixData.rightValue);
assertEquals(rightIntegerLiteral.tokenLiteral(), "" + infixData.rightValue);

assertTrue(infixExpression.getLeft() instanceof IntegerLiteral);
IntegerLiteral leftIntegerLiteral = (IntegerLiteral) infixExpression.getLeft();

assertEquals(leftIntegerLiteral.getValue(), infixData.leftValue);
assertEquals(leftIntegerLiteral.tokenLiteral(), "" + infixData.leftValue);
}

}

private class PrefixDataBoolean
{
public String input;
public String operator;
public boolean value;
public PrefixDataBoolean(String input, String operator, boolean value) {
this.input = input;
this.operator = operator;
this.value = value;
}
}

@Test
public void testParsingBooleanPrefixExpression() {
List<PrefixDataBoolean> inputs = Arrays.asList(
new PrefixDataBoolean("!true;", "!", true),
new PrefixDataBoolean("!false;", "!", false));
for(PrefixDataBoolean prefixData : inputs) {
Lexer lexer = new Lexer(prefixData.input);
Parser parser = new Parser(lexer);
Program program = parser.parseProgram();
checkForParseErrors(parser, prefixData.input);

assertEquals(program.getStatementsLength(), 1);
assertTrue(program.getStatements().get(0) instanceof ExpressionStatement);
ExpressionStatement statement = (ExpressionStatement) program.getStatements().get(0);
assertTrue(statement.getExpression() instanceof PrefixExpression);
PrefixExpression prefixExpression = (PrefixExpression) statement.getExpression();
assertEquals(prefixExpression.getOperator(), prefixData.operator);

// TODO: testIntegerLiteral method for later
assertTrue(prefixExpression.getRight() instanceof BooleanType);
BooleanType booleanLiteral = (BooleanType) prefixExpression.getRight();

assertEquals(booleanLiteral .getValue(), prefixData.value);
assertEquals(booleanLiteral .tokenLiteral(), "" + prefixData.value);
}
}

private class PrefixDataInteger
{
public String input;
public String operator;
public int integerValue;
public PrefixData(String input, String operator, int integerValue) {
public PrefixDataInteger(String input, String operator, int integerValue) {
this.input = input;
this.operator = operator;
this.integerValue = integerValue;
}
}

@Test
public void testParsingPrefixExpression() {
List<PrefixData> inputs = Arrays.asList(new PrefixData("!5;", "!", 5), new PrefixData("-15;", "-", 15));
for(PrefixData prefixData : inputs) {
public void testParsingIntegerPrefixExpression() {
List<PrefixDataInteger> inputs = Arrays.asList(
new PrefixDataInteger("!5;", "!", 5),
new PrefixDataInteger("-15;", "-", 15));
for(PrefixDataInteger prefixData : inputs) {
Lexer lexer = new Lexer(prefixData.input);
Parser parser = new Parser(lexer);
Program program = parser.parseProgram();
checkForParseErrors(parser);
checkForParseErrors(parser, prefixData.input);

assertEquals(program.getStatementsLength(), 1);
assertTrue(program.getStatements().get(0) instanceof ExpressionStatement);
Expand Down Expand Up @@ -186,21 +339,31 @@ public void testOperatorPrecedenceParsing() {
new OperatorPrecedenceParsing("3 + 4; -5 * 5", "(3 + 4)((-5) * 5)"),
new OperatorPrecedenceParsing("5 < 4 == 3 < 4", "((5 < 4) == (3 < 4))"),
new OperatorPrecedenceParsing("5 > 4 != 3 > 4", "((5 > 4) != (3 > 4))"),
new OperatorPrecedenceParsing("3 + 4 * 5 == 3 * 1 + 4 * 5", "((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))")
new OperatorPrecedenceParsing("3 + 4 * 5 == 3 * 1 + 4 * 5", "((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))"),
new OperatorPrecedenceParsing("true", "true"),
new OperatorPrecedenceParsing("false", "false"),
new OperatorPrecedenceParsing("3 > 5 == false", "((3 > 5) == false)"),
new OperatorPrecedenceParsing("3 < 5 == true", "((3 < 5) == true)"),
new OperatorPrecedenceParsing("1 + (2 + 3) + 4", "((1 + (2 + 3)) + 4)"),
new OperatorPrecedenceParsing("(5 + 5) * 2", "((5 + 5) * 2)"),
new OperatorPrecedenceParsing("2 / (5 + 5)", "(2 / (5 + 5))"),
new OperatorPrecedenceParsing("-(5 + 5)", "(-(5 + 5))"),
new OperatorPrecedenceParsing("!(true == true)", "(!(true == true))")

);

for (OperatorPrecedenceParsing operatorPrecedenceParsing : operatorPrecedenceParsingList) {
String input = operatorPrecedenceParsing.input;
Lexer lexer = new Lexer(input);
Parser parser = new Parser(lexer);
Program program = parser.parseProgram();
checkForParseErrors(parser);
checkForParseErrors(parser, operatorPrecedenceParsing.input);

assertEquals(program.string(), operatorPrecedenceParsing.expected);
}
}

private void checkForParseErrors(Parser parser) {
assertEquals(parser.getErrors().size(), 0, "There should not be any errors: " + parser.getErrors());
private void checkForParseErrors(Parser parser, String input) {
assertEquals(parser.getErrors().size(), 0, "Input [" + input + "] should not gives errors. There should not be any errors: " + parser.getErrors());
}
}

0 comments on commit 2866846

Please sign in to comment.