From 4c496ebbe24805899f85c20f195ea0b741cadcf5 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Mon, 8 Jan 2018 01:26:00 +0300 Subject: [PATCH] Improved switch transpilation. Fix #531 (#534) --- ast/util.go | 8 +++- tests/switch.c | 103 ++++++++++++++++++++++++++++++++++++++++++- transpiler/switch.go | 84 +++++++++++++++++++++++++++++++++-- 3 files changed, 189 insertions(+), 6 deletions(-) diff --git a/ast/util.go b/ast/util.go index a1a147089..07a6b6f7d 100644 --- a/ast/util.go +++ b/ast/util.go @@ -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 } diff --git a/tests/switch.c b/tests/switch.c index 34af1f589..05a6c73f2 100644 --- a/tests/switch.c +++ b/tests/switch.c @@ -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(); @@ -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(); } diff --git a/transpiler/switch.go b/transpiler/switch.go index 6e3376cec..e9abf9d6b 100644 --- a/transpiler/switch.go +++ b/transpiler/switch.go @@ -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 @@ -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