Skip to content

Commit

Permalink
Improved switch transpilation. Fix #531 (#534)
Browse files Browse the repository at this point in the history
  • Loading branch information
Konstantin8105 authored and elliotchance committed Jan 7, 2018
1 parent 853a41c commit 4c496eb
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 6 deletions.
8 changes: 7 additions & 1 deletion ast/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,15 @@ func Atos(node Node) string {
var str string
str += fmt.Sprint("==== START OF AST tree ====\n")
str += out.String()
str += TypesTree(node)
str += fmt.Sprint("==== END OF AST tree ====\n")
return str
}

// TypesTree - return tree of types for AST node
func TypesTree(node Node) (str string) {
str += fmt.Sprintf("\nTypes tree:\n")
str += typesTree(node, 0)
str += fmt.Sprint("==== END OF AST tree ====\n")
return str
}

Expand Down
103 changes: 102 additions & 1 deletion tests/switch.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,105 @@ void scoped_fallthrough_several_cases_including_default()
}
}

typedef struct I67 I67;
struct I67{
int x,y;
};

void run( I67 * i ,int pos)
{
switch (pos) {
case 0:
(*i).x += 1;
(*i).y += 1;
break;
case 1:
(*i).x -= 1;
(*i).y -= 1;
break;
}
}

void run_with_block( I67 * i ,int pos)
{
switch (pos) {
case 0:
{
(*i).x += 1;
(*i).y += 1;
break;
}
case 1:
{
(*i).x -= 1;
(*i).y -= 1;
}
break;
case 2:
(*i).x *= 1;
(*i).y *= 1;
break;
default:
(*i).x *= 10;
(*i).y *= 10;
}
}

void switch_issue67()
{
I67 i;
i.x = 0;
i.y = 0;
run(&i, 0);
is_eq(i.x, 1);
is_eq(i.y, 1);
run(&i, 1);
is_eq(i.x, 0);
is_eq(i.y, 0);
run_with_block(&i,0);
is_eq(i.x, 1);
is_eq(i.y, 1);
run_with_block(&i, 1);
is_eq(i.x, 0);
is_eq(i.y, 0);
}

void empty_switch()
{
int pos = 0;
switch (pos){
}
is_eq(pos,0);
}

void default_only_switch()
{
int pos = 0;
switch (pos){
case -1: // empty case
case -1-4: // empty case
case (-1-4-4): // empty case
case (-3): // empty case
case -2: // empty case
default:
pos++;
}
is_eq(pos,1);
}

void switch_without_input()
{
int pos = 0;
switch (0){
default:
pos++;
}
is_eq(pos,1);
}

int main()
{
plan(14);
plan(25);

match_a_single_case();
fallthrough_to_next_case();
Expand All @@ -227,5 +323,10 @@ int main()
scoped_match_default();
scoped_fallthrough_several_cases_including_default();

switch_issue67();
empty_switch();
default_only_switch();
switch_without_input();

done_testing();
}
84 changes: 80 additions & 4 deletions transpiler/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ import (
)

func transpileSwitchStmt(n *ast.SwitchStmt, p *program.Program) (
*goast.SwitchStmt, []goast.Stmt, []goast.Stmt, error) {
preStmts := []goast.Stmt{}
postStmts := []goast.Stmt{}
_ *goast.SwitchStmt, preStmts []goast.Stmt, postStmts []goast.Stmt, err error) {
defer func() {
if err != nil {
err = fmt.Errorf("Cannot transpileSwitchStmt : err = %v", err)
}
}()

// The first two children are nil. I don't know what they are supposed to be
// for. It looks like the number of children is also not reliable, but we
Expand All @@ -36,9 +39,82 @@ func transpileSwitchStmt(n *ast.SwitchStmt, p *program.Program) (

preStmts, postStmts = combinePreAndPostStmts(preStmts, postStmts, newPre, newPost)

// separation body of switch on cases
body := n.Children()[len(n.Children())-1].(*ast.CompoundStmt)

// solving switch case without body
// case -1:
// default: ...
checkAgain:
for i := range body.Children() {
if v, ok := body.Children()[i].(*ast.CaseStmt); ok {
if vv, ok := v.Children()[len(v.Children())-1].(*ast.CaseStmt); ok {
body.AddChild(vv)
v.Children()[len(v.Children())-1] = &ast.CompoundStmt{}
goto checkAgain
}
if vv, ok := v.Children()[len(v.Children())-1].(*ast.DefaultStmt); ok {
body.AddChild(vv)
v.Children()[len(v.Children())-1] = &ast.CompoundStmt{}
goto checkAgain
}
}
}

for i := range body.Children() {
// For simplification - each CaseStmt will have CompoundStmt
if v, ok := body.Children()[i].(*ast.CaseStmt); ok {
if _, ok := v.Children()[len(v.Children())-1].(*ast.CompoundStmt); !ok {
var compoundStmt ast.CompoundStmt
compoundStmt.AddChild(v.Children()[len(v.Children())-1])
v.Children()[len(v.Children())-1] = &compoundStmt
}
}
// For simplification - each DefaultStmt will have CompoundStmt
if v, ok := body.Children()[i].(*ast.DefaultStmt); ok {
if _, ok := v.Children()[len(v.Children())-1].(*ast.CompoundStmt); !ok {
var compoundStmt ast.CompoundStmt
compoundStmt.AddChild(v.Children()[len(v.Children())-1])
v.Children()[len(v.Children())-1] = &compoundStmt
}
}
}

// Move element inside CompoundStmt
for i := 0; i < len(body.Children()); i++ {
switch body.Children()[i].(type) {
case *ast.CaseStmt, *ast.DefaultStmt:
// do nothing
default:
if i != 0 {
lastStmt := body.Children()[i-1].Children()
if comp, ok := lastStmt[len(lastStmt)-1].(*ast.CompoundStmt); ok {
// add node in CompoundStmt
comp.AddChild(body.Children()[i])

// remove from body
if i+1 < len(body.Children()) {
body.ChildNodes = append(body.ChildNodes[:i], body.ChildNodes[i+1:]...)
} else {
body.ChildNodes = body.ChildNodes[:i]
}

// goto to last iteration
i--
} else {
p.AddMessage(p.GenerateWarningMessage(
fmt.Errorf("Unexpected element"), n))
}
} else {
p.AddMessage(p.GenerateWarningMessage(
fmt.Errorf("Unsupport case"), n))
}

}
}

// The body will always be a CompoundStmt because a switch statement is not
// valid without curly brackets.
body := n.Children()[len(n.Children())-1].(*ast.CompoundStmt)
cases, newPre, newPost, err := normalizeSwitchCases(body, p)
if err != nil {
return nil, nil, nil, err
Expand Down

0 comments on commit 4c496eb

Please sign in to comment.