diff --git a/fortran/function_definition.go b/fortran/function_definition.go index c524f226..395c2d81 100644 --- a/fortran/function_definition.go +++ b/fortran/function_definition.go @@ -9,23 +9,33 @@ type callArgumentSimplification struct { } func (c callArgumentSimplification) Visit(node goast.Node) (w goast.Visitor) { - // from : &((*a)) - // to : a - if id, ok := node.(*goast.Ident); ok { - if len(id.Name) > 6 && id.Name[:4] == "&((*" { - id.Name = id.Name[4 : len(id.Name)-2] - } + id, ok := node.(*goast.Ident) + if !ok { + return c + } - if len(id.Name) > 11 && id.Name[:11] == "*func()*int" { - // *func()*int{y:=6;return &y}() - id.Name = id.Name[15:] - id.Name = id.Name[:len(id.Name)-13] - } - if len(id.Name) > 8 && id.Name[:8] == "*func()*" { - // TODO : for other types - // fmt.Println("Simply : ", id.Name) - } + if len(id.Name) > 6 && id.Name[:4] == "&((*" { + // from : &((*a)) + // to : a + id.Name = id.Name[4 : len(id.Name)-2] + return c + } + + if !strings.Contains(id.Name, "*func()") { + return c + } + + // *func()*int{y:=6;return &y}() + // *func()*byte{y:=byte('K');return &y}() + index := strings.Index(id.Name, "=") + if index < 0 { + return c + } + last := strings.LastIndex(id.Name, ";") + if last < 0 { + return c } + id.Name = id.Name[index+1 : last] return c } @@ -34,59 +44,77 @@ type intrinsic struct { p *parser } +func isGoFunc(name string) bool { + var list = [...]string{ + "panic", + "make", + "real", + "append", + } + for i := range list { + if list[i] == name { + return true + } + } + return false +} + func (in intrinsic) Visit(node goast.Node) (w goast.Visitor) { - if call, ok := node.(*goast.CallExpr); ok { - if n, ok := call.Fun.(*goast.Ident); ok { - if f, ok := intrinsicFunction[strings.ToUpper(n.Name)]; ok { - f(in.p, call) - } else if n.Name != "make" && n.Name != "append" && n.Name != "panic" { - n.Name = strings.ToUpper(n.Name) - } + call, ok := node.(*goast.CallExpr) + if !ok { + return in + } + + if n, ok := call.Fun.(*goast.Ident); ok { + if f, ok := intrinsicFunction[strings.ToUpper(n.Name)]; ok { + f(in.p, call) + } else if !isGoFunc(n.Name) { + n.Name = strings.ToUpper(n.Name) } } - if call, ok := node.(*goast.CallExpr); ok { - if sel, ok := call.Fun.(*goast.SelectorExpr); ok { - if x, ok := sel.X.(*goast.Ident); ok && x.Name == "intrinsic" { - var isRead bool = sel.Sel.Name == "READ" + if sel, ok := call.Fun.(*goast.SelectorExpr); ok { + if x, ok := sel.X.(*goast.Ident); ok && x.Name == "intrinsic" { - for i := range call.Args { - if isRead && i > 1 { - // for READ command other arguments is pointer always + var isRead bool = sel.Sel.Name == "READ" + + for i := range call.Args { + if isRead && i > 1 { + // for READ command other arguments is pointer always + continue + } + if arg, ok := call.Args[i].(*goast.Ident); ok { + if len(arg.Name) > 3 && arg.Name[:2] == "&(" { + arg.Name = arg.Name[2 : len(arg.Name)-1] continue } - if arg, ok := call.Args[i].(*goast.Ident); ok { - if len(arg.Name) > 3 && arg.Name[:2] == "&(" { - arg.Name = arg.Name[2 : len(arg.Name)-1] - continue - } - if strings.Contains(arg.Name, "func()*[]byte{y:=[]byte(") { - arg.Name = arg.Name[17:] - index := strings.LastIndex(arg.Name, "\")") - arg.Name = arg.Name[:index+2] - if i > 1 { - arg.Name = arg.Name[7 : len(arg.Name)-1] - } - continue - } - if len(arg.Name) > 10 && arg.Name[:7] == "func()*" { - arg.Name = "*" + arg.Name - continue + if strings.Contains(arg.Name, "func()*[]byte{y:=[]byte(") { + arg.Name = arg.Name[17:] + index := strings.LastIndex(arg.Name, "\")") + arg.Name = arg.Name[:index+2] + if i > 1 { + arg.Name = arg.Name[7 : len(arg.Name)-1] } + continue } - if un, ok := call.Args[i].(*goast.UnaryExpr); ok { - if par, ok := un.X.(*goast.ParenExpr); ok { - if id, ok := par.X.(*goast.IndexExpr); ok { - call.Args[i] = id - continue - } + if len(arg.Name) > 10 && arg.Name[:7] == "func()*" { + arg.Name = "*" + arg.Name + continue + } + } + if un, ok := call.Args[i].(*goast.UnaryExpr); ok { + if par, ok := un.X.(*goast.ParenExpr); ok { + if id, ok := par.X.(*goast.IndexExpr); ok { + call.Args[i] = id + continue } } } } } } + return in } @@ -169,6 +197,11 @@ var intrinsicFunction = map[string]func(*parser, *goast.CallExpr){ p.addImport("github.com/Konstantin8105/f4go/intrinsic") intrinsicArgumentCorrection(p, f, "intrinsic.EPSILON", typeNames) }, + "CMPLX": func(p *parser, f *goast.CallExpr) { + typeNames := []string{"any"} + p.addImport("github.com/Konstantin8105/f4go/intrinsic") + intrinsicArgumentCorrection(p, f, "intrinsic.CMPLX", typeNames) + }, "SQRT": func(p *parser, f *goast.CallExpr) { typeNames := []string{"any"} p.addImport("github.com/Konstantin8105/f4go/intrinsic") diff --git a/fortran/parser.go b/fortran/parser.go index 71a4bb4b..98d3f8ae 100644 --- a/fortran/parser.go +++ b/fortran/parser.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" goast "go/ast" + "go/format" goparser "go/parser" "go/token" "os" @@ -247,6 +248,8 @@ func Parse(b []byte, packageName string) (_ goast.File, errs []error) { p.ident = 0 decls = p.parseNodes() + p.correctCellVector(decls) + // add packages for pkg := range p.pkgs { p.ast.Decls = append(p.ast.Decls, &goast.GenDecl{ @@ -344,6 +347,116 @@ func Parse(b []byte, packageName string) (_ goast.File, errs []error) { return p.ast, p.errs } +type callCorrection struct { + args map[string][]*goast.Field + p *parser +} + +func (cc callCorrection) Visit(node goast.Node) (w goast.Visitor) { + call, ok := node.(*goast.CallExpr) + if !ok { + return cc + } + + ident, ok := call.Fun.(*goast.Ident) + if !ok { + return cc + } + + name := ident.Name + + args, ok := cc.args[name] + if !ok { + return cc + } + + if len(args) != len(call.Args) { + return cc + } + + for i := range call.Args { + var count int + goast.Inspect(call.Args[i], func(node goast.Node) bool { + if _, ok := node.(*goast.IndexExpr); ok { + count++ + } + return true + }) + ident, ok := args[i].Type.(*goast.Ident) + if !ok { + continue + } + nameCount := strings.Count(ident.Name, "[") + if count == nameCount { + continue + } + if count < nameCount { + continue + } + if nameCount == 0 { + continue + } + if nameCount != 1 { + // TODO: not clear - is it happend? + continue + } + // preliminary code: + // CTEST(M[2][3][4]) + // ---------- + // CELL + // + // transform cell to vector + // CTEST((*[1000000]float64)(unsafe.Pointer(M[2][3][4]))[:]) + // ------- ---------- + // TYPE CELL + + var cell string // M[2][3][4] + { + // func Fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) error + var buf bytes.Buffer + err := format.Node(&buf, token.NewFileSet(), call.Args[i]) + if err != nil { + panic(err) + } + cell = buf.String() + } + + var typ = args[i].Type.(*goast.Ident).Name // "*[ ]complex128" + var typSize = strings.Replace(typ, "[", "[1000000", -1) + + // func () ( output *[]TYPE) { + // output = (*[1000000]TYPE)(unsafe.Pointer(M[2][3][4]))[:] + // return + // } + + call.Args[i] = goast.NewIdent(fmt.Sprintf(` func () ( _ %s) { output := (%s)(unsafe.Pointer(%s))[:]; return &output}() `, typ, typSize, cell)) + cc.p.addImport("unsafe") + } + + return cc +} + +func (p *parser) correctCellVector(decls []goast.Decl) { + // get all goast.FuncDecl arguments + var cc callCorrection + cc.args = map[string][]*goast.Field{} + cc.p = p + for i := range decls { + decl, ok := decls[i].(*goast.FuncDecl) + if !ok { + continue + } + name := decl.Name.Name + typ := decl.Type.Params.List + cc.args[name] = typ + } + + // iteration by all goast.CallExpr + for i := range decls { + goast.Walk(cc, decls[i]) + } +} + // go/ast Visitor for comment label type commentLabel struct { labels map[string]bool @@ -891,10 +1004,7 @@ func (c callArg) Visit(node goast.Node) (w goast.Visitor) { } if call, ok := node.(*goast.CallExpr); ok { if id, ok := call.Fun.(*goast.Ident); ok { - if id.Name == "append" { - return nil - } - if id.Name == "panic" { + if isGoFunc(id.Name) { return nil } } @@ -940,11 +1050,12 @@ func (c callArg) Visit(node goast.Node) (w goast.Visitor) { } case *goast.CallExpr: - // var ident goast.Ident + // var ident *goast.Ident // ident, ok := a.Fun.(*goast.Ident) - // if ok { + // if !ok { // continue // } + // fmt.Println(">>>>>>", ident) // returnType, ok := p.functionReturnType[ident.Name] // if ok { // continue diff --git a/intrinsic/math.go b/intrinsic/math.go index e484be11..04e5d0d3 100644 --- a/intrinsic/math.go +++ b/intrinsic/math.go @@ -40,6 +40,11 @@ func castToFloat64(w interface{}) float64 { } } +func CMPLX(a interface{}) complex128 { + A := castToFloat64(a) + return complex(A, 0) +} + func SQRT(a interface{}) float64 { A := castToFloat64(a) return math.Sqrt(A) @@ -86,8 +91,8 @@ func DBLE(a interface{}) float64 { panic(fmt.Errorf("Cannot find type : %T", a)) } -func ABS(a float64) float64 { - return math.Abs(a) +func ABS(a interface{}) float64 { + return math.Abs(castToFloat64(a)) } func CABS(a complex128) float64 { diff --git a/main.go b/main.go index 2078ca10..3a105298 100644 --- a/main.go +++ b/main.go @@ -15,11 +15,19 @@ import ( "github.com/Konstantin8105/f4go/fortran" ) -var packageFlag *string +var ( + packageFlag string + verboseFlag bool +) func main() { - packageFlag = flag.String("p", - "main", "set the name of the generated package") + pntPack := &packageFlag + pntPack = flag.String("p", "main", "set the name of the generated package") + _ = pntPack + + pntVerbose := &verboseFlag + pntVerbose = flag.Bool("v", false, "verbose all stages of transpilation and all errors") + _ = pntVerbose run() } @@ -35,14 +43,11 @@ func run() { return } - if packageFlag == nil { - var s string - packageFlag = &s - } - - es := parseParallel(flag.Args(), *packageFlag) - for _, e := range es { - fmt.Printf("%20s : %s\n", e.filename, e.err.Error()) + es := parseParallel(flag.Args(), packageFlag) + if verboseFlag { + for _, e := range es { + fmt.Printf("%20s : %s\n", e.filename, e.err.Error()) + } } } @@ -51,6 +56,10 @@ type errorRow struct { filename string } +func (e errorRow) Error() string { + return fmt.Sprintf("%s : %v", e.filename, e.err) +} + // parsing to Go code func parse(filename, packageName, goFilename string) (errR []errorRow) { if packageName == "" { diff --git a/main_test.go b/main_test.go index 0d23112e..80cdde08 100644 --- a/main_test.go +++ b/main_test.go @@ -122,7 +122,14 @@ func ShowDiff(a, b string) string { func TestFail(t *testing.T) { - fortran.Debug = testing.Verbose() + { + oldVerbose := verboseFlag + defer func() { + verboseFlag = oldVerbose + }() + fortran.Debug = testing.Verbose() + verboseFlag = testing.Verbose() + } // wrong source errs := parse("./testdata/fortran_fail.f", "", "") @@ -181,7 +188,7 @@ func getFortranTestFiles(dir string) (files []string, err error) { return } -func parsingBlas(filename string) (failed bool) { +func parsingBlas(filename string) (err error) { filename = "./" + filename // Go name @@ -192,7 +199,17 @@ func parsingBlas(filename string) (failed bool) { goname = strings.Replace(goname, "SRC", "TESTING", 1) // parse - return len(parse(filename, "main", goname)) > 0 + errR := parse(filename, "main", goname) + if len(errR) == 0 { + return nil + } + + var str string + for i := range errR { + str += errR[i].Error() + "\n" + } + + return fmt.Errorf(str) } func TestBlas(t *testing.T) { @@ -215,9 +232,10 @@ func TestBlas(t *testing.T) { var amount int for i := range ss { - failed := parsingBlas(ss[i]) - if failed { + err := parsingBlas(ss[i]) + if err != nil { t.Logf("Error is not empty in file: %s", ss[i]) + t.Logf("%v", err) amount++ } } @@ -249,7 +267,7 @@ func TestBlas(t *testing.T) { } } if !found { - t.Errorf("Cannot find fortran line %d: %s", i, fortran[i]) + t.Errorf("Cannot find FORTRAN line %d: %s", i, fortran[i]) } } @@ -264,7 +282,7 @@ func TestBlas(t *testing.T) { } } if !found { - t.Errorf("Cannot find go line %d: %s", i, gof[i]) + t.Errorf("Cannot find GO line %d: %s", i, gof[i]) } } } @@ -312,9 +330,10 @@ func TestBlasTesting(t *testing.T) { for i := range tcs { t.Run(fmt.Sprintf("TESTING%3d", i), func(t *testing.T) { for j := range tcs[i].fortranFiles { - failed := parsingBlas(tcs[i].fortranFiles[j]) - if failed { + err := parsingBlas(tcs[i].fortranFiles[j]) + if err != nil { t.Logf("failed file: %s", tcs[i].fortranFiles[j]) + t.Logf("%v", err) } } @@ -493,7 +512,14 @@ func TestComments(t *testing.T) { func TestCrash(t *testing.T) { - fortran.Debug = testing.Verbose() + { + oldVerbose := verboseFlag + defer func() { + verboseFlag = oldVerbose + }() + fortran.Debug = testing.Verbose() + verboseFlag = testing.Verbose() + } var ( in = "./testdata/min_crash.f" diff --git a/testdata/main.f b/testdata/main.f index 0f26c735..9926f6ea 100644 --- a/testdata/main.f +++ b/testdata/main.f @@ -298,6 +298,20 @@ subroutine test_do() integer IR, JR, HR, iterator integer ab_min, funarr, funcell integer A(2,2) + INTEGER NP1, INCX + COMPLEX CTRUE5(8,5,2) + + iterator = 9 + DO IR = 1,8 + DO JR = 1,5 + DO HR = 1,2 + CTRUE5(IR,JR,HR) = CMPLX(iterator) + iterator = iterator + 3 + WRITE (*,'(F8.2)') REAL(CTRUE5(IR,JR,HR)) + END DO + END DO + END DO + JR = 1 iterator = 1 DO 140 IR = 1,2 @@ -353,6 +367,12 @@ subroutine test_do() GO TO 144 END IF call test_do2() + + NP1 = 1 + INCX = 1 + CALL CTEST(CTRUE5(1,NP1,INCX)) + + return 142 FORMAT ('Do with inc ', I2) 146 FORMAT ('Do ', I2) @@ -363,6 +383,16 @@ subroutine test_do() 151 FORMAT (' iterator = ', I2) end + SUBROUTINE CTEST(STRUE) + COMPLEX STRUE(20) + Integer i + DO i = 1,20 + WRITE (*,* ) "CTEST" + WRITE (*,'(I2)' ) I + WRITE (*,'(F8.2)') REAL(STRUE(I)) + ENd do + END SUBROUTINE + SUBROUTINE funcwrt(LEN, a) INTEGER LEN INTEGER a(LEN)