Skip to content

Commit

Permalink
Add default case to switch statements (#126)
Browse files Browse the repository at this point in the history
* add token

* add default case to parser

* evaluate default cases
  • Loading branch information
kaidesu committed Oct 27, 2023
1 parent 2a9f0fa commit 270e246
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 32 deletions.
14 changes: 14 additions & 0 deletions evaluator/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ func evaluateSwitch(node *ast.Switch, scope *object.Scope) object.Object {
obj := Evaluate(node.Value, scope)

for _, option := range node.Cases {
// Skip default case to handle last if needed
if option.Default {
continue
}

for _, val := range option.Value {
out := Evaluate(val, scope)

Expand All @@ -22,5 +27,14 @@ func evaluateSwitch(node *ast.Switch, scope *object.Scope) object.Object {
}
}

// Handle default case
for _, option := range node.Cases {
if option.Default {
out := evaluateBlock(option.Body, scope)

return out
}
}

return nil
}
35 changes: 13 additions & 22 deletions examples/switch.ghost
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
// value = true

// switch (value) {
// case false {
// print("false.")
// }

// case true {
// print("true.")
// }
// }

beverage = "coffee"
beverage = "tea"

switch (beverage) {
case "water" {
print("Water is $0.75 per bottle.")
}
case "juice" {
print("Juice is $1.25 per bottle.")
}
case "coffee", "latte" {
print("Coffee and lattes are $2.75 per 12oz.")
}
case "water" {
print("Water is $0.75 per bottle.")
}
case "juice" {
print("Juice is $1.25 per bottle.")
}
case "coffee", "latte" {
print("Coffee and lattes are $2.75 per 12oz.")
}
default {
print("Unknown beverage.")
}
}
95 changes: 95 additions & 0 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,101 @@ func TestReturnStatements(t *testing.T) {
}
}

func TestSwitchStatements(t *testing.T) {
input := `switch (value) {
case 1 {
print('one')
}
case 2 {
print('two')
}
}`

scanner := scanner.New(input, "test.ghost")
parser := New(scanner)
program := parser.Parse()

failIfParserHasErrors(t, parser)

statement, ok := program.Statements[0].(*ast.Expression)

if !ok {
t.Fatalf("program.Statements[0] is not ast.Expression. got=%T", program.Statements[0])
}

switchStatement, ok := statement.Expression.(*ast.Switch)

if !ok {
t.Fatalf("statement is not ast.Switch. got=%T", statement.Expression)
}

if len(switchStatement.Cases) != 2 {
t.Fatalf("switchStatement.Cases has wrong length. got=%d", len(switchStatement.Cases))
}
}

func TestSwitchStatementsWithDefault(t *testing.T) {
input := `switch (value) {
case 1 {
print('one')
}
case 2 {
print('two')
}
default {
print('default')
}
}`

scanner := scanner.New(input, "test.ghost")
parser := New(scanner)
program := parser.Parse()

failIfParserHasErrors(t, parser)

statement, ok := program.Statements[0].(*ast.Expression)

if !ok {
t.Fatalf("program.Statements[0] is not ast.Expression. got=%T", program.Statements[0])
}

switchStatement, ok := statement.Expression.(*ast.Switch)

if !ok {
t.Fatalf("statement is not ast.Switch. got=%T", statement.Expression)
}

if len(switchStatement.Cases) != 3 {
t.Fatalf("switchStatement.Cases has wrong length. got=%d", len(switchStatement.Cases))
}
}

func TestSwitchStatementsWithMultipleDefaults(t *testing.T) {
input := `switch (value) {
case 1 {
print('one')
}
case 2 {
print('two')
}
case default {
print('default one')
}
default {
print('default two')
}
}`

scanner := scanner.New(input, "test.ghost")
parser := New(scanner)
parser.Parse()

// Expecting a parser error here for having multiple defaults
if len(parser.Errors()) != 1 {
t.Fatalf("parser should have 1 error. got=%d", len(parser.Errors()))
}
}

// =============================================================================
// Helper methods

Expand Down
43 changes: 33 additions & 10 deletions parser/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,34 @@ func (parser *Parser) switchStatement() ast.ExpressionNode {

for !parser.currentTokenIs(token.RIGHTBRACE) {
// check for EOF
//
if parser.currentTokenIs(token.EOF) {
return nil
}

switchCase := &ast.Case{Token: parser.currentToken}

if parser.currentTokenIs(token.CASE) {
if parser.currentTokenIs(token.DEFAULT) {
switchCase.Default = true
} else if parser.currentTokenIs(token.CASE) {
// read "case"
parser.readToken()

// A switch case can contain multiple "values"
switchCase.Value = append(switchCase.Value, parser.parseExpression(LOWEST))
// Allow "case default" to be valid
if parser.currentTokenIs(token.DEFAULT) {
switchCase.Default = true
} else {
// A switch case can contain multiple "values"
switchCase.Value = append(switchCase.Value, parser.parseExpression(LOWEST))

for parser.nextTokenIs(token.COMMA) {
// read the comma
parser.readToken()
for parser.nextTokenIs(token.COMMA) {
// read the comma
parser.readToken()

// setup the expression
parser.readToken()
// setup the expression
parser.readToken()

switchCase.Value = append(switchCase.Value, parser.parseExpression(LOWEST))
switchCase.Value = append(switchCase.Value, parser.parseExpression(LOWEST))
}
}
}

Expand All @@ -75,5 +84,19 @@ func (parser *Parser) switchStatement() ast.ExpressionNode {
return nil
}

// Check for multiple default cases
defaultCount := 0

for _, switchCase := range expression.Cases {
if switchCase.Default {
defaultCount++
}
}

if defaultCount > 1 {
parser.errors = append(parser.errors, "multiple default cases in switch statement")
return nil
}

return expression
}
1 change: 1 addition & 0 deletions scanner/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var keywords = map[string]token.Type{
"case": token.CASE,
"class": token.CLASS,
"continue": token.CONTINUE,
"default": token.DEFAULT,
"else": token.ELSE,
"extends": token.EXTENDS,
"false": token.FALSE,
Expand Down
1 change: 1 addition & 0 deletions token/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const (
CASE = "case"
CLASS = "class"
CONTINUE = "continue"
DEFAULT = "default"
ELSE = "else"
EXTENDS = "extends"
FALSE = "false"
Expand Down

0 comments on commit 270e246

Please sign in to comment.