|
@@ -385,6 +385,122 @@ func TestBooleanExpression(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+func TestIfExpression(t *testing.T) {
|
|
|
+ // Notice: no semicolon, no return.
|
|
|
+ input := `if (x < y) { x }`
|
|
|
+
|
|
|
+ l := lexer.New(input)
|
|
|
+ p := New(l)
|
|
|
+ program := p.ParseProgram()
|
|
|
+ checkParserErrors(t, p)
|
|
|
+
|
|
|
+ if len(program.Statements) != 1 {
|
|
|
+ t.Fatalf("program.Statements does not contain %d statements. got=%d\n",
|
|
|
+ 1, len(program.Statements))
|
|
|
+ }
|
|
|
+
|
|
|
+ // This is the external statement (if) not the one in Consequence (block).
|
|
|
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
|
|
|
+ if !ok {
|
|
|
+ t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
|
|
|
+ program.Statements[0])
|
|
|
+ }
|
|
|
+
|
|
|
+ exp, ok := stmt.Expression.(*ast.IfExpression)
|
|
|
+ if !ok {
|
|
|
+ t.Fatalf("stmt.Expression is not ast.IfExpression. got=%T",
|
|
|
+ stmt.Expression)
|
|
|
+ }
|
|
|
+
|
|
|
+ if !testInfixExpression(t, exp.Condition, "x", "<", "y") {
|
|
|
+ // No error message, they have already been generated in testInfixExpression.
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(exp.Consequence.Statements) != 1 {
|
|
|
+ t.Fatalf("consequence is not 1 statements. got=%d\n",
|
|
|
+ len(exp.Consequence.Statements))
|
|
|
+ }
|
|
|
+
|
|
|
+ consequence, ok := exp.Consequence.Statements[0].(*ast.ExpressionStatement)
|
|
|
+ if !ok {
|
|
|
+ t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T",
|
|
|
+ exp.Consequence.Statements[0])
|
|
|
+ }
|
|
|
+
|
|
|
+ if !testIdentifier(t, consequence.Expression, "x") {
|
|
|
+ // No error message, they have already been generated in testIdentifier.
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if exp.Alternative != nil {
|
|
|
+ t.Errorf("exp.Alternative.Statements was not nil. got=%+v",
|
|
|
+ exp.Alternative)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestIfElseExpression(t *testing.T) {
|
|
|
+ input := `if (x < y) { x } else { y }`
|
|
|
+
|
|
|
+ l := lexer.New(input)
|
|
|
+ p := New(l)
|
|
|
+ program := p.ParseProgram()
|
|
|
+ checkParserErrors(t, p)
|
|
|
+
|
|
|
+ if len(program.Statements) != 1 {
|
|
|
+ t.Fatalf("program.Statements does not contain %d statements. got=%d\n",
|
|
|
+ 1, len(program.Statements))
|
|
|
+ }
|
|
|
+
|
|
|
+ // This is the external statement (if) not the one in Consequence (block).
|
|
|
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
|
|
|
+ if !ok {
|
|
|
+ t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
|
|
|
+ program.Statements[0])
|
|
|
+ }
|
|
|
+
|
|
|
+ exp, ok := stmt.Expression.(*ast.IfExpression)
|
|
|
+ if !ok {
|
|
|
+ t.Fatalf("stmt.Expression is not ast.IfExpression. got=%T",
|
|
|
+ stmt.Expression)
|
|
|
+ }
|
|
|
+
|
|
|
+ if !testInfixExpression(t, exp.Condition, "x", "<", "y") {
|
|
|
+ // No error message, they have already been generated in testInfixExpression.
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(exp.Consequence.Statements) != 1 {
|
|
|
+ t.Errorf("consequence is not 1 statements. got=%d\n",
|
|
|
+ len(exp.Consequence.Statements))
|
|
|
+ }
|
|
|
+
|
|
|
+ consequence, ok := exp.Consequence.Statements[0].(*ast.ExpressionStatement)
|
|
|
+ if !ok {
|
|
|
+ t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T",
|
|
|
+ exp.Consequence.Statements[0])
|
|
|
+ }
|
|
|
+
|
|
|
+ if !testIdentifier(t, consequence.Expression, "x") {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(exp.Alternative.Statements) != 1 {
|
|
|
+ t.Errorf("exp.Alternative.Statements does not contain 1 statements. got=%d\n",
|
|
|
+ len(exp.Alternative.Statements))
|
|
|
+ }
|
|
|
+
|
|
|
+ alternative, ok := exp.Alternative.Statements[0].(*ast.ExpressionStatement)
|
|
|
+ if !ok {
|
|
|
+ t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T",
|
|
|
+ exp.Alternative.Statements[0])
|
|
|
+ }
|
|
|
+
|
|
|
+ if !testIdentifier(t, alternative.Expression, "y") {
|
|
|
+ return
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
func testLetStatement(t *testing.T, s ast.Statement, name string) bool {
|
|
|
if s.TokenLiteral() != "let" {
|
|
|
t.Errorf("s.TokenLiteral not 'let'. got=%q", s.TokenLiteral())
|