Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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