Konstantin8105/f4go

View on GitHub
fortran/parser.go

Summary

Maintainability
F
1 wk
Test Coverage
package fortran

import (
    "bytes"
    "fmt"
    goast "go/ast"
    goparser "go/parser"
    "go/token"
    "runtime/debug"
    "strconv"
    "strings"
)

type varInitialization struct {
    name string
    typ  goType
}

type varInits []varInitialization

func (v varInits) get(n string) (varInitialization, bool) {
    n = strings.ToUpper(n)
    for _, val := range []varInitialization(v) {
        if val.name == n {
            return val, true
        }
    }
    return varInitialization{}, false
}

func (v *varInits) del(n string) {
    vs := []varInitialization(*v)
    n = strings.ToUpper(n)
    for i, val := range vs {
        if val.name == n {
            vs = append(vs[:i], vs[i+1:]...)
            *v = varInits(vs)
            return
        }
    }
}

func (v *varInits) add(name string, typ goType) {
    vs := []varInitialization(*v)
    vs = append(vs, varInitialization{name: strings.ToUpper(name), typ: typ})
    *v = varInits(vs)
}

func (p parser) getSize(name string, col int) (size int, ok bool) {
    v, ok := p.initVars.get(name)
    if !ok {
        panic("Cannot find variable : " + name)
    }
    if v.typ.baseType == "string" {
        col++
    }
    if len(v.typ.arrayNode[col]) == 1 && v.typ.arrayNode[col][0].tok == token.INT {
        val, _ := strconv.Atoi(string(v.typ.arrayNode[col][0].b))
        return val, true
    }
    if vv, ok := p.initVars.get(nodesToString(v.typ.arrayNode[col])); ok {
        if n, ok := p.constants[vv.name]; ok {
            size, _ = strconv.Atoi(nodesToString(n))
            return size, true
        }
    }
    for i, n := range v.typ.arrayNode[col] {
        // Example:
        // -1 : 1
        // Nodes:
        // [[-, `-`] [INT, `1`] [:, `:`] [INT, `1`]]
        // 99 : 101
        // Nodes:
        // [[INT, `99`] [:, `:`] [INT, `101`]]
        if n.tok == token.COLON {
            begin, err := strconv.Atoi(strings.Replace(nodesToString(v.typ.arrayNode[col][:i]), " ", "", -1))
            if err != nil {
                p.addError("Cannot parse begin value : " + nodesToString(v.typ.arrayNode[col][:i]))
                break
            }
            end, err := strconv.Atoi(strings.Replace(nodesToString(v.typ.arrayNode[col][i+1:]), " ", "", -1))
            if err != nil {
                p.addError("Cannot parse end value : " + nodesToString(v.typ.arrayNode[col][i+1:]))
                break
            }
            return end - begin + 1, true
        }
    }
    return -1, false
}

func (p parser) getArrayBegin(name string, col int) int {
    v, ok := p.initVars.get(name)
    if !ok {
        panic("Cannot find variable : " + name)
    }
    if v.typ.baseType == "string" {
        col++
    }
    if col >= len(v.typ.arrayNode) {
        return 1
    }
    for i, n := range v.typ.arrayNode[col] {
        // Example:
        // -1 : 1
        // Nodes:
        // [[-, `-`] [INT, `1`] [:, `:`] [INT, `1`]]
        // 99 : 101
        // Nodes:
        // [[INT, `99`] [:, `:`] [INT, `101`]]
        if n.tok == token.COLON {
            strBegin := strings.Replace(nodesToString(v.typ.arrayNode[col][:i]), " ", "", -1)
            b, err := strconv.Atoi(strBegin)
            if err != nil {
                p.addError("Cannot parse begin value: " + strBegin)
            }
            return b
        }
    }
    return 1
}

func (p parser) getArrayLen(name string) int {
    v, ok := p.initVars.get(name)
    if !ok {
        panic("Cannot find variable : " + name)
    }
    lenArray := len(v.typ.arrayNode)
    //     if v.typ.baseType == "string" {
    //         lenArray--
    //     }
    return lenArray
}

type common struct {
    mem map[string][]varInitialization
}

func (c *common) addBlockName(name string, vars []varInitialization) {
    if c.mem == nil {
        c.mem = map[string][]varInitialization{}
    }
    // common may add new vars
    if v, ok := c.mem[name]; ok {
        for i := range v {
            found := false
            for j := range vars {
                if v[i].name == vars[j].name {
                    found = true
                }
            }
            if found {
                continue
            }
            vars = append(vars, v[i])
        }
    }
    c.mem[name] = vars
}

type implicitVariable struct {
    symbol byte
    typ    []node
}

type parser struct {
    ast   goast.File
    ident int
    ns    []node

    Common common // share memory between subroutines

    implicit []implicitVariable

    functionExternalName []string

    initVars varInits // map of name to type

    comments []string

    pkgs        map[string]bool // import packages
    endLabelDo  map[string]int  // label of DO
    allLabels   map[string]bool // list of all labels
    foundLabels map[string]bool // list labels found in source

    parameters map[string]string // constants

    formats map[string][]node // source line with command FORMAT

    constants map[string][]node

    errs []error
}

func (p *parser) addImport(pkg string) {
    p.pkgs[pkg] = true
}

func (p *parser) init() {
    p.functionExternalName = make([]string, 0)
    p.endLabelDo = map[string]int{}
    p.allLabels = map[string]bool{}
    p.foundLabels = map[string]bool{}
    p.initVars = varInits{}
    p.parameters = map[string]string{}
    p.formats = map[string][]node{}
    p.implicit = nil
    p.constants = map[string][]node{}
}

// list view - only for debugging
func lv(ns []node) (output string) {
    for _, n := range ns {
        b := string(n.b)
        if n.tok != ftNewLine {
            output += fmt.Sprintf("%10s\t%10s\t|`%s`\n",
                view(n.tok),
                fmt.Sprintf("%v", n.pos),
                b)
        } else {
            output += fmt.Sprintf("%20s\n",
                view(n.tok))
        }
    }
    return
}

// Parse is convert fortran source to go ast tree
func Parse(b []byte, packageName string) (_ goast.File, errs []error) {

    if packageName == "" {
        packageName = "main"
    }

    var p parser

    if p.pkgs == nil {
        p.pkgs = map[string]bool{}
    }

    p.ns = scan(b)

    p.ast.Name = goast.NewIdent(packageName)

    var decls []goast.Decl
    p.ident = 0
    decls = p.parseNodes()

    // add packages
    for pkg := range p.pkgs {
        p.ast.Decls = append(p.ast.Decls, &goast.GenDecl{
            Tok: token.IMPORT,
            Specs: []goast.Spec{
                &goast.ImportSpec{
                    Path: &goast.BasicLit{
                        Kind:  token.STRING,
                        Value: "\"" + pkg + "\"",
                    },
                },
            },
        })
    }

    // TODO : add INTRINSIC fortran functions

    // put COMMON
    if len(p.Common.mem) > 0 {
        var fields []*goast.Field
        for names, vars := range p.Common.mem {
            var nameFields []*goast.Field
            for i := range vars {
                var (
                    varName = vars[i].name
                    varType = []byte(vars[i].typ.String())
                )
                // from:
                //
                // [3]int
                // [1]float64
                // [2][2][2]float64
                // float64
                // int
                //
                // to:
                //
                // []int
                // []float64
                // [][][]float64
                // float64
                // int
                isopen := false
                for i := range varType {
                    if varType[i] == '[' {
                        isopen = true
                        continue
                    }
                    if varType[i] == ']' {
                        isopen = false
                        continue
                    }
                    if !isopen {
                        continue
                    }
                    varType[i] = ' '
                }
                nameFields = append(nameFields, &goast.Field{
                    Names: []*goast.Ident{goast.NewIdent(varName)},
                    Type:  goast.NewIdent("*" + string(varType)),
                })
            }
            fields = append(fields, &goast.Field{
                Names: []*goast.Ident{goast.NewIdent(names)},
                Type:  &goast.StructType{Fields: &goast.FieldList{List: nameFields}},
            })
        }

        p.ast.Decls = append(p.ast.Decls, &goast.GenDecl{
            Tok: token.TYPE,
            Specs: []goast.Spec{
                &goast.TypeSpec{
                    Name: goast.NewIdent("MEMORY"),
                    Type: &goast.StructType{Fields: &goast.FieldList{List: fields}},
                },
            },
        })

        p.ast.Decls = append(p.ast.Decls, &goast.GenDecl{
            Tok: token.VAR,
            Specs: []goast.Spec{
                &goast.ValueSpec{
                    Names: []*goast.Ident{goast.NewIdent("COMMON")},
                    Type:  goast.NewIdent("MEMORY"),
                },
            },
        })
    }

    p.ast.Decls = append(p.ast.Decls, decls...)

    strC := strChanger{}
    goast.Walk(strC, &p.ast)

    return p.ast, p.errs
}

// go/ast Visitor for comment label
type commentLabel struct {
    labels map[string]bool
}

func (c commentLabel) Visit(node goast.Node) (w goast.Visitor) {
    if ident, ok := node.(*goast.Ident); ok && ident != nil {
        if _, ok := c.labels[ident.Name]; ok {
            ident.Name = "//" + ident.Name
        }
    }
    return c
}

// go/ast Visitor for change "strings" to "[]byte"
type strChanger struct {
}

func (s strChanger) Visit(node goast.Node) (w goast.Visitor) {
    if call, ok := node.(*goast.CallExpr); ok {
        if sel, ok := call.Fun.(*goast.SelectorExpr); ok {
            if id, ok := sel.X.(*goast.Ident); ok {
                if id.Name == "fmt" || id.Name == "math" {
                    return nil
                }
            }
        }
        if ident, ok := call.Fun.(*goast.Ident); ok {
            if ident.Name == "panic" {
                return nil
            }
        }
    }
    if _, ok := node.(*goast.ImportSpec); ok {
        return nil
    }
    if st, ok := node.(*goast.BasicLit); ok && st.Kind == token.STRING {
        if len(st.Value) == 3 {
            st.Kind = token.CHAR
            st.Value = fmt.Sprintf("'%c'", st.Value[1])
        } else {
            st.Value = fmt.Sprintf("*func()*[]byte{y:=[]byte(%s);return &y}()",
                st.Value)
        }
    }
    return s
}

// parseNodes
func (p *parser) parseNodes() (decls []goast.Decl) {
    Debugf("Parse nodes")

    if p.ident < 0 || p.ident >= len(p.ns) {
        p.errs = append(p.errs,
            fmt.Errorf("Ident is outside nodes: %d/%d", p.ident, len(p.ns)))
        return
    }

    // find all names of FUNCTION, SUBROUTINE, PROGRAM
    var internalFunction []string
    for ; p.ident < len(p.ns); p.ident++ {
        switch p.ns[p.ident].tok {
        case ftSubroutine:
            p.expect(ftSubroutine)
            p.ident++
            if p.ns[p.ident].tok == token.IDENT {
                p.expect(token.IDENT)
            }
            internalFunction = append(internalFunction, string(p.ns[p.ident].b))
            continue
        case ftProgram:
            p.expect(ftProgram)
            p.ident++
            if p.ns[p.ident].tok == token.IDENT {
                p.expect(token.IDENT)
            }
            internalFunction = append(internalFunction, string(p.ns[p.ident].b))
            continue
        }

        // Example:
        //   RECURSIVE SUBROUTINE CGELQT3( M, N, A, LDA, T, LDT, INFO )
        if strings.ToUpper(string(p.ns[p.ident].b)) == "RECURSIVE" {
            p.ns[p.ident].tok, p.ns[p.ident].b = ftNewLine, []byte("\n")
            continue
        }

        // FUNCTION
        for i := p.ident; i < len(p.ns) && p.ns[i].tok != ftNewLine; i++ {
            if p.ns[p.ident].tok == ftFunction {
                p.expect(ftFunction)
                p.ident++
                p.expect(token.IDENT)
                internalFunction = append(internalFunction, string(p.ns[p.ident].b))
            }
        }
    }
    p.ident = 0

    for ; p.ident < len(p.ns); p.ident++ {
        p.init()
        p.functionExternalName = append(p.functionExternalName,
            internalFunction...)

        var next bool
        switch p.ns[p.ident].tok {
        case ftDefine:
            p.addError("Cannot parse #DEFINE: " + p.getLine())
            p.gotoEndLine()
            continue

        case ftNewLine:
            next = true // TODO
        case token.COMMENT:
            p.comments = append(p.comments,
                "//"+string(p.ns[p.ident].b))
            next = true // TODO
        case ftSubroutine: // SUBROUTINE
            var decl goast.Decl
            decl = p.parseSubroutine()
            decls = append(decls, decl)
            next = true
        case ftProgram: // PROGRAM
            var decl goast.Decl
            decl = p.parseProgram()
            decls = append(decls, decl)
            next = true
        default:
            // Example :
            //  COMPLEX FUNCTION CDOTU ( N , CX , INCX , CY , INCY )
            for i := p.ident; i < len(p.ns) && p.ns[i].tok != ftNewLine; i++ {
                if p.ns[i].tok == ftFunction {
                    decl := p.parseFunction()
                    decls = append(decls, decl)
                    next = true
                }
            }
        }
        if next {
            continue
        }

        if p.ident >= len(p.ns) {
            break
        }

        switch p.ns[p.ident].tok {
        case ftNewLine, token.EOF:
            continue
        }

        // if at the begin we haven't SUBROUTINE , FUNCTION,...
        // then add fake Program
        var comb []node
        comb = append(comb, p.ns[:p.ident]...)
        comb = append(comb, []node{
            {tok: ftNewLine, b: []byte("\n")},
            {tok: ftProgram, b: []byte("PROGRAM")},
            {tok: token.IDENT, b: []byte("MAIN")},
            {tok: ftNewLine, b: []byte("\n")},
        }...)
        comb = append(comb, p.ns[p.ident:]...)
        p.ns = comb
        p.ident--

        Infof("Add fake PROGRAM MAIN in pos : %v", p.ns[p.ident].pos)
    }

    return
}

func (p *parser) gotoEndLine() {
    for ; p.ident < len(p.ns) && p.ns[p.ident].tok != ftNewLine; p.ident++ {
    }
}

func (p *parser) getLine() (line string) {
    if p.ident < 0 {
        p.ident = 0
    }
    if !(p.ident < len(p.ns)) {
        p.ident = len(p.ns) - 1
    }

    last := p.ident
    defer func() {
        p.ident = last
    }()
    for ; p.ident >= 0 && p.ns[p.ident].tok != ftNewLine; p.ident-- {
    }
    p.ident++
    for ; p.ident < len(p.ns) && p.ns[p.ident].tok != ftNewLine; p.ident++ {
        line += " " + string(p.ns[p.ident].b)
    }
    return
}

// go/ast Visitor for parse FUNCTION
type vis struct {
    // map [from] to
    c map[string]string
}

func initVis() *vis {
    var v vis
    v.c = map[string]string{}
    return &v
}

func (v vis) Visit(node goast.Node) (w goast.Visitor) {
    if ident, ok := node.(*goast.Ident); ok {
        if to, ok := v.c[strings.ToUpper(ident.Name)]; ok {
            ident.Name = "(" + to + ")"
        }
    }
    return v
}

// delete external function type definition
func (p *parser) removeExternalFunction() {
    for _, f := range p.functionExternalName {
        p.initVars.del(f)
    }
}

// add correct type of subroutine arguments
func (p *parser) argumentCorrection(fd goast.FuncDecl) (removedVars []string) {
checkArguments:
    for i := range fd.Type.Params.List {
        fieldName := fd.Type.Params.List[i].Names[0].Name
        if v, ok := p.initVars.get(fieldName); ok {
            fd.Type.Params.List[i].Type = goast.NewIdent(v.typ.String())

            // Remove to arg
            removedVars = append(removedVars, fieldName)
            p.initVars.del(fieldName)
            goto checkArguments
        }
    }
    return
}

type replacer struct {
    from, to string
}

func (r replacer) Visit(node goast.Node) (w goast.Visitor) {
    if node != nil {
        if ident, ok := node.(*goast.Ident); ok {
            node.(*goast.Ident).Name = strings.Replace(ident.Name, r.from, r.to, -1)
        }
    }
    return r
}

// init vars
func (p *parser) initializeVars() (vars []goast.Stmt) {
    defer func() {
        if r := recover(); r != nil {
            err := fmt.Sprintf("Recover initializeVars : %v", r)
            Debugf("%s", err)
            p.addError(err)
            p.gotoEndLine()
        }
    }()
    for i := range []varInitialization(p.initVars) {
        name := ([]varInitialization(p.initVars)[i]).name
        assign := strings.Contains(name, "COMMON.") || strings.Contains(name, returnPostfix)
        goT := ([]varInitialization(p.initVars)[i]).typ
        switch p.getArrayLen(name) {
        case 0:

            fset := token.NewFileSet() // positions are relative to fset
            src := `package main
func main() {
    MATRIX := new(%s)
}
`
            var (
                subName = "MATRIX"
                typ     = goT.getBaseType()
            )
            s := fmt.Sprintf(src, typ)
            f, err := goparser.ParseFile(fset, "", s, 0)
            if err != nil {
                panic(fmt.Errorf("Error: %v\nSource:\n%s\npos=%s",
                    err, s, goT.arrayNode))
            }
            var r replacer
            r.from = subName
            r.to = name
            goast.Walk(r, f)

            list := f.Decls[0].(*goast.FuncDecl).Body.List
            if assign {
                list[0].(*goast.AssignStmt).Tok = token.ASSIGN
            }

            vars = append(vars, list...)

        case 1: // vector

            fset := token.NewFileSet() // positions are relative to fset
            src := `package main
func main() {
    MATRIX := func() (*[]%s){ arr:=make([]%s, %d); return &arr}()
}
`
            var (
                subName  = "MATRIX"
                size0, _ = p.getSize(name, 0)
                typ      = goT.getBaseType()
            )
            s := fmt.Sprintf(src, typ, typ, size0)
            f, err := goparser.ParseFile(fset, "", s, 0)
            if err != nil {
                panic(fmt.Errorf("Error: %v\nSource:\n%s\npos=%s",
                    err, s, goT.arrayNode))
            }
            var r replacer
            r.from = subName
            r.to = name
            goast.Walk(r, f)

            list := f.Decls[0].(*goast.FuncDecl).Body.List
            if assign {
                list[0].(*goast.AssignStmt).Tok = token.ASSIGN
            }

            vars = append(vars, list...)

        case 2: // matrix

            fset := token.NewFileSet() // positions are relative to fset
            src := `package main
func main() {
    MATRIX := func()(*[][]%s){
        arr := make([][]%s, %d)
        for u := 0; u < %d; u++ {
            arr[u] = make([]%s, %d)
        }
        return &arr
    }()
}
`
            var (
                subName  = "MATRIX"
                size0, _ = p.getSize(name, 0)
                size1, _ = p.getSize(name, 1)
                typ      = goT.getBaseType()
            )
            s := fmt.Sprintf(src, typ, typ, size0, size0, typ, size1)
            f, err := goparser.ParseFile(fset, "", s, 0)
            if err != nil {
                panic(fmt.Errorf("Error: %v\nSource:\n%s\npos=%s",
                    err, s, goT.arrayNode))
            }
            var r replacer
            r.from = subName
            r.to = name
            goast.Walk(r, f)

            list := f.Decls[0].(*goast.FuncDecl).Body.List
            if assign {
                list[0].(*goast.AssignStmt).Tok = token.ASSIGN
            }

            vars = append(vars, list...)

        case 3: // ()()()
            fset := token.NewFileSet() // positions are relative to fset
            src := `package main
func main() {
    MATRIX := func()(*[][][]%s) {
        arr := make([][][]%s, %d)
        for u := 0; u < %d; u++ {
            arr[u] = make([][]%s, %d)
            for w := 0; w < %d; w++ {
                arr[u][w] = make([]%s, %d)
            }
        }
        return &arr
    }()
}
`
            var (
                subName  = "MATRIX"
                size0, _ = p.getSize(name, 0)
                size1, _ = p.getSize(name, 1)
                size2, _ = p.getSize(name, 2)
                typ      = goT.getBaseType()
            )
            s := fmt.Sprintf(src, typ, typ, size0, size0, typ, size1, size1, typ, size2)
            f, err := goparser.ParseFile(fset, "", s, 0)
            if err != nil {
                panic(fmt.Errorf("Error: %v\nSource:\n%s\npos=%s",
                    err, s, goT.arrayNode))
            }

            var r replacer
            r.from = subName
            r.to = name
            goast.Walk(r, f)

            list := f.Decls[0].(*goast.FuncDecl).Body.List
            if assign {
                list[0].(*goast.AssignStmt).Tok = token.ASSIGN
            }

            vars = append(vars, list...)

        case 4: // ()()()()
            fset := token.NewFileSet() // positions are relative to fset
            src := `package main
func main() {
    MATRIX := make([][][][]%s, %d)
    for u := 0; u < %d; u++ {
        MATRIX[u] = make([][][]%s, %d)
        for w := 0; w < %d; w++ {
            MATRIX[u][w] = make([][]%s, %d)
            for q := 0; q < %d; q++{
                MATRIX[u][w][q] = make([]%s, %d)
            }
        }
    }
}
`
            var (
                subName  = "MATRIX"
                size0, _ = p.getSize(name, 0)
                size1, _ = p.getSize(name, 1)
                size2, _ = p.getSize(name, 2)
                size3, _ = p.getSize(name, 3)
                typ      = goT.getBaseType()
            )
            s := fmt.Sprintf(src,
                typ, size0, size0,
                typ, size1, size1,
                typ, size2, size2,
                typ, size3,
            )
            f, err := goparser.ParseFile(fset, "", s, 0)
            if err != nil {
                panic(fmt.Errorf("Error: %v\nSource:\n%s\npos=%s",
                    err, s, goT.arrayNode))
            }

            var r replacer
            r.from = subName
            r.to = name
            goast.Walk(r, f)

            list := f.Decls[0].(*goast.FuncDecl).Body.List
            if assign {
                list[0].(*goast.AssignStmt).Tok = token.ASSIGN
            }

            vars = append(vars, list...)

        case 5: // ()()()()()
            fset := token.NewFileSet() // positions are relative to fset
            src := `package main
func main() {
    MATRIX := make([][][][][]%s, %d)
    for u := 0; u < %d; u++ {
        MATRIX[u] = make([][][][]%s, %d)
        for w := 0; w < %d; w++ {
            MATRIX[u][w] = make([][][]%s, %d)
            for q := 0; q < %d; q++{
                MATRIX[u][w][q] = make([][]%s, %d)
                for s := 0; s < %d; s++{
                    MATRIX[u][w][q][s] = make([]%s, %d)
                }
            }
        }
    }
}
`
            var (
                subName  = "MATRIX"
                size0, _ = p.getSize(name, 0)
                size1, _ = p.getSize(name, 1)
                size2, _ = p.getSize(name, 2)
                size3, _ = p.getSize(name, 3)
                size4, _ = p.getSize(name, 4)
                typ      = goT.getBaseType()
            )
            s := fmt.Sprintf(src,
                typ, size0, size0,
                typ, size1, size1,
                typ, size2, size2,
                typ, size3, size3,
                typ, size4,
            )
            f, err := goparser.ParseFile(fset, "", s, 0)
            if err != nil {
                panic(fmt.Errorf("Error: %v\nSource:\n%s\npos=%s",
                    err, s, goT.arrayNode))
            }

            var r replacer
            r.from = subName
            r.to = name
            goast.Walk(r, f)

            list := f.Decls[0].(*goast.FuncDecl).Body.List
            if assign {
                list[0].(*goast.AssignStmt).Tok = token.ASSIGN
            }

            vars = append(vars, list...)

        default:
            panic(fmt.Errorf(
                "not correct amount of array : %v", goT))
        }
    }

    return
}

// go/ast Visitor for comment label
type callArg struct {
    p *parser
}

func isIgnoreCall(call *goast.CallExpr) bool {
    if sel, ok := call.Fun.(*goast.SelectorExpr); ok {
        if name, ok := sel.X.(*goast.Ident); ok {
            switch name.Name {
            case
                "math",
                "real",
                "fmt":
                return true

            case
                "intrinsic":
                if sel.Sel.Name == "READ" {
                    return false
                }
                return true
            }
        }
    }

    if id, ok := call.Fun.(*goast.Ident); ok {
        switch id.Name {
        case "append",
            "panic":
            return true
        }
    }

    return false
}

// Example
//  From :
// ab_min(3, 14)
//  To:
// ab_min(func() *int { y := 3; return &y }(), func() *int { y := 14; return &y }())
func (c callArg) Visit(node goast.Node) goast.Visitor {
    call, ok := node.(*goast.CallExpr)
    if !ok {
        return c
    }
    if call == nil {
        return nil
    }
    if isIgnoreCall(call) {
        return c
    }

    for i := range call.Args {
        switch a := call.Args[i].(type) {
        case *goast.BasicLit:
            switch a.Kind {
            case token.STRING:
                call.Args[i] = goast.NewIdent(
                    fmt.Sprintf("func()*[]byte{y:=[]byte(%s);return &y}()", a.Value))
                if len(a.Value) == 3 {
                    a.Value = strings.Replace(a.Value, "\"", "'", -1)
                    call.Args[i] = goast.NewIdent(
                        fmt.Sprintf("func()*byte{y:=byte(%s);return &y}()", a.Value))
                }
            case token.INT:
                call.Args[i] = goast.NewIdent(
                    fmt.Sprintf("func()*int{y:=%s;return &y}()", a.Value))
            case token.FLOAT:
                call.Args[i] = goast.NewIdent(
                    fmt.Sprintf("func()*float64{y:=%s;return &y}()", a.Value))
            case token.CHAR:
                call.Args[i] = goast.NewIdent(
                    fmt.Sprintf("func()*byte{y:=%s;return &y}()", a.Value))
            default:
                panic(fmt.Errorf(
                    "Not support basiclit token: %T ", a.Kind))
            }

        case *goast.Ident, *goast.IndexExpr, *goast.ParenExpr:
            // from:  NAME
            // to  : &NAME
            call.Args[i] = &goast.UnaryExpr{
                Op: token.AND,
                X: &goast.ParenExpr{
                    Lparen: 1,
                    X:      call.Args[i],
                },
            }

        default:
            // TODO:
            // goast.Print(token.NewFileSet(), a)
            // panic(fmt.Errorf("Not support arg call token: %T ", a))
        }
    }

    return c
}

// Example :
//  COMPLEX FUNCTION CDOTU ( N , CX , INCX , CY , INCY )
//  DOUBLE PRECISION FUNCTION DNRM2 ( N , X , INCX )
//  COMPLEX * 16 FUNCTION ZDOTC ( N , ZX , INCX , ZY , INCY )
func (p *parser) parseFunction() (decl goast.Decl) {
    Debugf("Parse function")

    for i := p.ident; i < len(p.ns) && p.ns[i].tok != ftNewLine; i++ {
        if p.ns[i].tok == ftFunction {
            p.ns[i].tok = ftSubroutine
        }
    }
    return p.parseSubroutine()
}

// Example:
//   PROGRAM MAIN
func (p *parser) parseProgram() (decl goast.Decl) {
    Debugf("Parse program")

    p.expect(ftProgram)
    p.ns[p.ident].tok = ftSubroutine
    decl = p.parseSubroutine()
    if fd, ok := decl.(*goast.FuncDecl); ok {
        fd.Name.Name = "main"
    }
    return
}

const returnPostfix string = "_RETURN"

// parseSubroutine  is parsed SUBROUTINE, FUNCTION, PROGRAM
// Example :
//  SUBROUTINE CHBMV ( UPLO , N , K , ALPHA , A , LDA , X , INCX , BETA , Y , INCY )
//  PROGRAM MAIN
//  COMPLEX FUNCTION CDOTU ( N , CX , INCX , CY , INCY )
func (p *parser) parseSubroutine() (decl goast.Decl) {
    Debugf("Parse subroutine")

    defer func() {
        p.init()
    }()

    var fd goast.FuncDecl
    fd.Type = &goast.FuncType{
        Params: &goast.FieldList{},
    }

    defer func() {
        fd.Doc = &goast.CommentGroup{}
        for _, c := range p.comments {
            fd.Doc.List = append(fd.Doc.List, &goast.Comment{
                Text: c,
            })
        }
        p.comments = []string{}
    }()

    // check return type
    var returnType []node
    for ; p.ns[p.ident].tok != ftSubroutine && p.ns[p.ident].tok != ftNewLine; p.ident++ {
        returnType = append(returnType, p.ns[p.ident])
    }

    p.expect(ftSubroutine)

    p.ident++
    p.expect(token.IDENT)
    name := strings.ToUpper(string(p.ns[p.ident].b))
    fd.Name = goast.NewIdent(name)

    Debugf("subroutine name is : %s", name)

    // Add return type is exist
    returnName := name + returnPostfix
    if len(returnType) > 0 {
        typ := parseType(returnType)
        fd.Type.Results = &goast.FieldList{
            List: []*goast.Field{
                {
                    Names: []*goast.Ident{goast.NewIdent(returnName)},
                    Type:  goast.NewIdent("*" + typ.String()),
                },
            },
        }
        p.initVars.add(returnName, typ)
    }
    defer func() {
        // change function name variable to returnName
        if len(returnType) > 0 {
            v := initVis()
            v.c[name] = returnName
            goast.Walk(v, fd.Body)
        }
    }()

    // Parameters
    p.ident++
    fd.Type.Params.List = p.parseParamDecl()

    p.ident++
    fd.Body = &goast.BlockStmt{
        Lbrace: 1,
        List:   p.parseListStmt(),
    }

    // delete external function type definition
    p.removeExternalFunction()

    // remove from arguments arg with type string
    arrayArguments := map[string]bool{}
    for i := range fd.Type.Params.List {
        fieldName := fd.Type.Params.List[i].Names[0].Name
        if v, ok := p.initVars.get(fieldName); ok {
            if v.typ.isArray() {
                arrayArguments[fieldName] = true
            }
        }
    }

    // add correct type of subroutine arguments
    arguments := p.argumentCorrection(fd)

    // change arguments
    // From:
    //  a
    // To:
    //  *a
    v := initVis()
    for _, arg := range arguments {
        v.c[arg] = arg
    }
    goast.Walk(v, fd.Body)

    // changes arguments in func
    for i := range fd.Type.Params.List {
        switch fd.Type.Params.List[i].Type.(type) {
        case *goast.Ident:
            id := fd.Type.Params.List[i].Type.(*goast.Ident)
            {
                // remove sizes in matrix
                // from:
                // [2][2]int
                // to:
                // [][]int
                isopen := false
                b := []byte(id.Name)
                for i := range b {
                    if b[i] == '[' {
                        isopen = true
                        continue
                    }
                    if b[i] == ']' {
                        isopen = false
                        continue
                    }
                    if !isopen {
                        continue
                    }
                    b[i] = ' '
                }

                id.Name = string(b)
            }

            // add pointer
            id.Name = "*" + id.Name
        default:
            panic(fmt.Errorf("Cannot parse type in fields: %T",
                fd.Type.Params.List[i].Type))
        }
    }

    // replace call argument constants
    c := callArg{p: p}
    goast.Walk(c, fd.Body)

    // init vars
    fd.Body.List = append(p.initializeVars(), fd.Body.List...)

    // remove unused labels
    removedLabels := map[string]bool{}
    for k := range p.allLabels {
        if _, ok := p.foundLabels[k]; !ok {
            removedLabels[k] = true
        }
    }
    cl := commentLabel{labels: removedLabels}
    goast.Walk(cl, fd.Body)

    in := intrinsic{p: p}
    goast.Walk(in, fd.Body)

    var cas callArgumentSimplification
    goast.Walk(cas, fd.Body)

    decl = &fd
    return
}

func (p *parser) addError(msg string) {
    last := p.ident
    defer func() {
        p.ident = last
    }()

    p.errs = append(p.errs, fmt.Errorf("%s", msg))
}

func (p *parser) expect(t token.Token) {
    if t != p.ns[p.ident].tok {
        // Show all errors
        // No need show all error for avoid dublicates
        //    for _, err := range p.errs {
        //        fmt.Println("Error : ", err.Error())
        //    }
        // Panic
        panic(fmt.Errorf("Expect %s, but we have {{%s,%s}}. Pos = %v",
            view(t), view(p.ns[p.ident].tok), string(p.ns[p.ident].b),
            p.ns[p.ident].pos))
    }
}

func (p *parser) parseListStmt() (stmts []goast.Stmt) {
    for p.ident < len(p.ns) {
        // Only for debugging
        // fmt.Println("---------------")
        // for i := 0; i < len(p.ns); i++ {
        //     if p.ns[i].tok != ftNewLine {
        //         fmt.Printf("%8v %4d %3d %v %v\n", p.ident == i,
        //             i, p.ns[i].pos.line, p.ns[i].tok, string(p.ns[i].b))
        //         continue
        //     }
        //     fmt.Printf("%8v %4d %3d %v\n", p.ident == i,
        //         i, p.ns[i].pos.line, p.ns[i].tok)
        // }

        if p.ns[p.ident].tok == token.COMMENT {
            stmts = append(stmts, &goast.ExprStmt{
                X: goast.NewIdent("//" + string(p.ns[p.ident].b)),
            })
            p.ident++
            continue
        }
        if p.ns[p.ident].tok == ftNewLine {
            p.ident++
            continue
        }

        if p.ns[p.ident].tok == ftEnd {
            p.ident++
            p.gotoEndLine()
            // TODO need gotoEndLine() ??
            break
        }
        if p.ns[p.ident].tok == token.ELSE {
            // gotoEndLine() is no need for case:
            // ELSE IF (...)...
            break
        }

        stmt := p.parseStmt()
        if len(stmt) == 0 {
            // p.addError("stmt is nil in line ")
            // break
            continue
        }
        stmts = append(stmts, stmt...)
    }
    return
}

// Examples:
//  INTEGER INCX , INCY , N
//  COMPLEX CX ( * ) , CY ( * )
//  COMPLEX*16 A(LDA,*),X(*)
//  REAL A(LDA,*),B(LDB,*)
//  DOUBLE PRECISION DX(*)
//  LOGICAL CONJA,CONJB,NOTA,NOTB
//  CHARACTER*32 SRNAME
func (p *parser) parseInit() (stmts []goast.Stmt) {

    // parse base type
    var baseType []node
    for ; p.ns[p.ident].tok != token.IDENT; p.ident++ {
        baseType = append(baseType, p.ns[p.ident])
    }
    p.expect(token.IDENT)

    var name string
    var additionType []node
    for ; p.ns[p.ident].tok != ftNewLine &&
        p.ns[p.ident].tok != token.EOF; p.ident++ {
        // parse name
        p.expect(token.IDENT)
        name = string(p.ns[p.ident].b)

        // parse addition type
        additionType = []node{}
        p.ident++
        for ; p.ns[p.ident].tok != ftNewLine &&
            p.ns[p.ident].tok != token.EOF &&
            p.ns[p.ident].tok != token.COMMA; p.ident++ {
            if p.ns[p.ident].tok == token.LPAREN {
                counter := 0
                for ; ; p.ident++ {
                    switch p.ns[p.ident].tok {
                    case token.LPAREN:
                        counter++
                    case token.RPAREN:
                        counter--
                    case ftNewLine:
                        p.addError("Cannot parse type : not expected NEW_LINE")
                        return
                    }
                    if counter == 0 {
                        break
                    }
                    additionType = append(additionType, p.ns[p.ident])
                }
            }
            additionType = append(additionType, p.ns[p.ident])
        }

        // parse type = base type + addition type
        p.initVars.add(name, parseType(append(baseType, additionType...)))
        if p.ns[p.ident].tok != token.COMMA {
            p.ident--
        }
    }

    return
}

func (p *parser) parseDoWhile() (sDo goast.ForStmt) {
    p.expect(ftDo)
    p.ident++
    p.expect(ftWhile)
    p.ident++
    start := p.ident
    for ; p.ident < len(p.ns); p.ident++ {
        if p.ns[p.ident].tok == ftNewLine {
            break
        }
    }
    sDo.Cond = p.parseExpr(start, p.ident)

    p.expect(ftNewLine)
    p.ident++

    sDo.Body = &goast.BlockStmt{
        Lbrace: 1,
        List:   p.parseListStmt(),
    }

    return
}

// Examples:
// CALL XERBLA ( 'CGEMM ' , INFO )
// CALL NORET
func (p *parser) parseCall() goast.Stmt {
    {
        begin := p.ident
        p.expect(ftCall)
        p.ident++
        p.expect(token.IDENT)
        if name := strings.ToUpper(string(p.ns[p.ident].b)); name == "F4GOTESTOK" ||
            name == "F4GOTESTFAIL" {
            p.addImport("fmt")
            p.gotoEndLine()
            return &goast.ExprStmt{X: &goast.CallExpr{
                Fun: &goast.SelectorExpr{
                    X:   goast.NewIdent("fmt"),
                    Sel: goast.NewIdent("Printf"),
                },
                Args: []goast.Expr{&goast.BasicLit{
                    Kind:  token.STRING,
                    Value: "\" " + name + "\\n\"",
                },
                }}}
        }
        p.ident++
        start := p.ident

        // parse names and values
        var args [][]node
        {
            var nodes []node
            addNode := func() {
                args = append(args, nodes)
                nodes = make([]node, 0)
            }
            counter := 0
            for ; p.ident < len(p.ns); p.ident++ {
                if p.ns[p.ident].tok == ftNewLine {
                    break
                }
                if p.ns[p.ident].tok == token.COMMENT {
                    continue
                }
                if p.ns[p.ident].tok == token.LPAREN {
                    counter++
                    if counter == 1 {
                        continue
                    }
                }
                if p.ns[p.ident].tok == token.RPAREN {
                    counter--
                    if counter == 0 {
                        continue
                    }
                }
                if p.ns[p.ident].tok == token.COMMA && counter == 1 {
                    addNode()
                    continue
                }
                nodes = append(nodes, p.ns[p.ident])
            }
            addNode()
        }

        // Explode loops
        {
            var exp [][]node
            for i := range args {
                e, ok := explodeFor(args[i])
                if ok {
                    exp = append(exp, e...)
                    continue
                }
                exp = append(exp, args[i])
            }
            args = exp
        }

        // merge nodes
        {
            finish := p.ident
            var inject []node
            inject = append(inject, node{tok: token.LPAREN, b: []byte("(")})
            for i := range args {
                inject = append(inject, args[i]...)
                if i != len(args)-1 {
                    inject = append(inject, node{tok: token.COMMA, b: []byte(",")})
                }
            }
            inject = append(inject, node{tok: token.RPAREN, b: []byte(")")})

            p.ns = append(p.ns[:start], append(inject, p.ns[finish:]...)...)
            p.ident = begin
        }
    }

    p.expect(ftCall)
    p.ident++
    start := p.ident
    for ; p.ns[p.ident].tok != ftNewLine; p.ident++ {
    }
    f := p.parseExpr(start, p.ident)
    if p.ident-start == 1 {
        f = &goast.CallExpr{
            Fun: goast.NewIdent(string(p.ns[start].b)),
        }
    }
    p.expect(ftNewLine)
    return &goast.ExprStmt{
        X: f,
    }
}

func (p *parser) parseDo() (sDo goast.ForStmt) {
    p.expect(ftDo)
    p.ident++
    if p.ns[p.ident].tok == ftWhile {
        p.ident--
        return p.parseDoWhile()
    }
    // possible label
    if p.ns[p.ident].tok == token.INT {
        p.endLabelDo[string(p.ns[p.ident].b)]++
        p.ident++
    }
    // for case with comma "DO 40, J = 1, N"
    if p.ns[p.ident].tok == token.COMMA {
        p.ident++
    }

    p.expect(token.IDENT)
    name := p.ns[p.ident]

    p.ident++
    p.expect(token.ASSIGN)

    p.ident++
    // Init is expression
    start := p.ident
    counter := 0
    for ; p.ident < len(p.ns); p.ident++ {
        if p.ns[p.ident].tok == token.LPAREN {
            counter++
            continue
        }
        if p.ns[p.ident].tok == token.RPAREN {
            counter--
            continue
        }
        if p.ns[p.ident].tok == token.COMMA && counter == 0 {
            break
        }
    }
    sDo.Init = &goast.AssignStmt{
        Lhs: []goast.Expr{
            p.parseExprNodes([]node{name}),
        },
        Tok: token.ASSIGN, // =
        Rhs: []goast.Expr{
            p.parseExpr(start, p.ident),
        },
    }

    p.expect(token.COMMA)

    // Cond is expression
    p.ident++
    start = p.ident
    p.ns = append(p.ns[:start], append([]node{
        name,
        {tok: token.LEQ, b: []byte{'<', '='}},
    }, p.ns[start:]...)...)
    counter = 0
    for ; p.ident < len(p.ns); p.ident++ {
        if p.ns[p.ident].tok == token.LPAREN {
            counter++
            continue
        }
        if p.ns[p.ident].tok == token.RPAREN {
            counter--
            continue
        }
        if (p.ns[p.ident].tok == token.COMMA || p.ns[p.ident].tok == ftNewLine) &&
            counter == 0 {
            break
        }
    }
    sDo.Cond = p.parseBinary(start, p.ident)

    if p.ns[p.ident].tok == ftNewLine {
        sDo.Post = &goast.IncDecStmt{
            X:   p.parseExprNodes([]node{name}),
            Tok: token.INC,
        }
    } else {
        p.expect(token.COMMA)
        p.ident++

        // Post is expression
        start = p.ident
        for ; p.ident < len(p.ns); p.ident++ {
            if p.ns[p.ident].tok == ftNewLine {
                break
            }
        }
        sDo.Post = &goast.AssignStmt{
            Lhs: []goast.Expr{p.parseExprNodes([]node{name})},
            Tok: token.ADD_ASSIGN, // +=
            Rhs: []goast.Expr{p.parseExpr(start, p.ident)},
        }
    }

    p.expect(ftNewLine)

    sDo.Body = &goast.BlockStmt{
        Lbrace: 1,
        List:   p.parseListStmt(),
    }

    return
}

func (p *parser) parseBinary(start, finish int) (expr goast.Expr) {
    expr = p.parseExpr(start, finish)
    if b, ok := expr.(*goast.BinaryExpr); ok {
        if _, ok := b.X.(*goast.CallExpr); ok {
            b.X = &goast.ParenExpr{X: &goast.StarExpr{X: b.X}}
        }
        if _, ok := b.Y.(*goast.CallExpr); ok {
            b.Y = &goast.ParenExpr{X: &goast.StarExpr{X: b.Y}}
        }
    }
    return
}

func (p *parser) parseIf() (sIf goast.IfStmt) {
    p.ident++
    p.expect(token.LPAREN)

    p.ident++
    start := p.ident
    for counter := 1; p.ns[p.ident].tok != token.EOF; p.ident++ {
        var exit bool
        switch p.ns[p.ident].tok {
        case token.LPAREN:
            counter++
        case token.RPAREN:
            counter--
            if counter == 0 {
                exit = true
            }
        }
        if exit {
            break
        }
    }

    sIf.Cond = p.parseBinary(start, p.ident)

    p.expect(token.RPAREN)
    p.ident++

    if p.ns[p.ident].tok == ftThen {
        p.gotoEndLine()
        p.ident++
        sIf.Body = &goast.BlockStmt{
            Lbrace: 1,
            List:   p.parseListStmt(),
        }
    } else {
        sIf.Body = &goast.BlockStmt{
            Lbrace: 1,
            List:   p.parseStmt(),
        }
        return
    }

    if p.ident >= len(p.ns) {
        return
    }

    if p.ns[p.ident].tok == token.ELSE {
        p.ident++
        if p.ns[p.ident].tok == token.IF {
            ifr := p.parseIf()
            sIf.Else = &ifr
        } else {
            sIf.Else = &goast.BlockStmt{
                Lbrace: 1,
                List:   p.parseListStmt(),
            }
        }
    }

    return
}

func (p *parser) parseExternal() {
    p.expect(ftExternal)

    p.ident++
    for ; p.ns[p.ident].tok != token.EOF; p.ident++ {
        if p.ns[p.ident].tok == ftNewLine {
            p.ident++
            break
        }
        switch p.ns[p.ident].tok {
        case token.IDENT, ftInteger, ftReal, ftComplex:
            name := string(p.ns[p.ident].b)
            p.functionExternalName = append(p.functionExternalName, name)
        case token.COMMA:
            // ingore
        default:
            p.addError("Cannot parse External " + string(p.ns[p.ident].b))
        }
    }
}

func (p *parser) resetImplicit() {
    p.implicit = nil
}

func (p parser) isImplicit(b byte) (typ []node, ok bool) {
    for i := range p.implicit {
        if b == p.implicit[i].symbol {
            return p.implicit[i].typ, true
        }
    }
    return
}

func (p *parser) parseStmt() (stmts []goast.Stmt) {
    onlyForRecover := p.ident

    pos := p.ns[p.ident].pos

    defer func() {
        if r := recover(); r != nil {
            err := fmt.Sprintf("Recover parseStmt pos{%v}: %v", pos, r)
            Debugf("%s", err)

            p.addError("stacktrace from panic: \n" + string(debug.Stack()))
            p.addError(err)
            p.gotoEndLine()

            // generate as comment
            stmts = append(stmts, &goast.ExprStmt{
                X: goast.NewIdent("/" + "/" +
                    "F4GO: NOT IMPLEMENTED :" +
                    nodesToString(p.ns[onlyForRecover:p.ident])),
            })
        }
    }()

    switch p.ns[p.ident].tok {
    case ftInteger, ftCharacter, ftComplex, ftLogical, ftReal, ftDouble:
        stmts = append(stmts, p.parseInit()...)

    case ftEquivalence:
        p.addError(p.getLine())
        p.gotoEndLine()

    case ftRewind:
        s := p.parseRewind()
        stmts = append(stmts, s...)

    case ftDimension:
        // from:
        // DIMENSION M(100)
        // to:
        // INTEGER M(100)
        //
        // from:
        // IMPLICIT REAL M
        // DIMENSION M(100)
        // to:
        // REAL M(100)
        p.expect(ftDimension)
        p.ident++
        p.expect(token.IDENT)
        name := string(p.ns[p.ident].b)
        if typ, ok := p.isImplicit(p.ns[p.ident].b[0]); ok {
            p.ident++
            for ; p.ident < len(p.ns) && p.ns[p.ident].tok != ftNewLine; p.ident++ {
                typ = append(typ, p.ns[p.ident])
            }
            p.initVars.add(name, parseType(typ))
        } else {
            p.ident -= 1
            p.expect(ftDimension)
            p.ns[p.ident].tok = ftInteger
            p.ns[p.ident].b = []byte("INTEGER")
            return p.parseStmt()
        }

    case ftFormat:
        stmts = append(stmts, &goast.ExprStmt{
            X: goast.NewIdent("// Unused by f4go : " + p.getLine()),
        })
        p.gotoEndLine()

    case ftCommon:
        s := p.parseCommon()
        stmts = append(stmts, s...)

    case token.RETURN:
        stmts = append(stmts, &goast.ReturnStmt{})
        p.gotoEndLine()
        p.expect(ftNewLine)

    case ftParameter:
        //  PARAMETER ( ONE = ( 1.0E+0 , 0.0E+0 )  , ZERO = 0.0E+0 )
        s := p.parseParameter()
        stmts = append(stmts, s...)

    case ftOpen:
        s := p.parseOpen()
        stmts = append(stmts, s...)

    case ftRead:
        s := p.parseRead()
        stmts = append(stmts, s...)

    case ftClose:
        s := p.parseClose()
        stmts = append(stmts, s...)

    case ftAssign:
        s := p.parseAssign()
        stmts = append(stmts, s...)

    case ftDefine:
        p.addError("#DEFINE is not support :" + p.getLine())
        p.gotoEndLine()

    case ftSave:
        p.expect(ftSave)
        // ignore command SAVE
        // that command only for optimization
        p.gotoEndLine()

    case ftExternal:
        p.parseExternal()

    case ftNewLine:
        // ignore
        p.ident++

    case token.IF:
        sIf := p.parseIf()
        stmts = append(stmts, &sIf)

    case ftDo:
        sDo := p.parseDo()
        stmts = append(stmts, &sDo)

    case ftCall:
        // Example:
        // CALL XERBLA ( 'CGEMM ' , INFO )
        sCall := p.parseCall()
        stmts = append(stmts, sCall)

    case ftIntrinsic:
        // Example:
        //  INTRINSIC CONJG , MAX
        p.expect(ftIntrinsic)
        p.ns[p.ident].tok = ftExternal
        p.parseExternal()

    case ftData:
        // Example:
        // DATA GAM , GAMSQ , RGAMSQ / 4096.D0 , 16777216.D0 , 5.9604645D-8 /
        sData := p.parseData()
        stmts = append(stmts, sData...)

    case ftWrite:
        stmts = append(stmts,
            &goast.ExprStmt{
                X: goast.NewIdent("// Unused by f4go : " + p.getLine()),
            },
            &goast.ExprStmt{
                goast.NewIdent("fmt.Println(\"WRITE SOMETHING\")"),
            },
        )
        p.gotoEndLine()

    case ftPrint:
        stmts = append(stmts, &goast.ExprStmt{
            X: goast.NewIdent("// Unused by f4go : " + p.getLine()),
        })
        p.gotoEndLine()

    case ftStop:
        p.expect(ftStop)
        p.ident++
        var msg []byte
        for ; p.ident < len(p.ns) && p.ns[p.ident].tok != ftNewLine; p.ident++ {
            msg = append(msg, p.ns[p.ident].b...)
        }
        stmts = append(stmts, &goast.ExprStmt{
            X: &goast.CallExpr{
                Fun:    goast.NewIdent("panic"),
                Lparen: 1,
                Args: []goast.Expr{
                    &goast.BasicLit{
                        Kind:  token.STRING,
                        Value: "\"" + string(msg) + "\"",
                    },
                },
            },
        })

    case token.GOTO:
        // Examples:
        //  GO TO 30
        //  GO TO ( 40, 80 )IEXC
        // TODO: go to next,(30, 50, 70, 90, 110)
        sGoto := p.parseGoto()
        stmts = append(stmts, sGoto...)
        stmts = append(stmts, p.convertLineToComment()...)
        p.expect(ftNewLine)

    case ftImplicit:
        // Examples:
        //    IMPLICIT DOUBLE PRECISION A
        //    IMPLICIT INTEGER B
        //
        // Only with one symbol name
        p.expect(ftImplicit)
        p.ident++

        var typ []node
        for ; p.ident < len(p.ns); p.ident++ {
            if p.ns[p.ident].tok == ftNewLine || p.ns[p.ident].tok == token.EOF {
                break
            }
            typ = append(typ, p.ns[p.ident])
        }
        impl := implicitVariable{
            symbol: typ[len(typ)-2].b[0],
            typ:    typ[:len(typ)-3],
        }
        p.implicit = append(p.implicit, impl)
        p.expect(ftNewLine)

    case token.INT:
        labelName := string(p.ns[p.ident].b)
        // From:
        //
        // DO 300 J = 1,PS
        // 300 TS = TS+J
        //
        // To:
        //
        // DO 300 J = 1,PS
        // TS = TS+J
        // 300 END
        //
        if v, ok := p.endLabelDo[labelName]; ok && 0 < v {
            if p.ns[p.ident+1].tok != ftEnd && p.ns[p.ident+1].tok != token.CONTINUE {
                // go to END
                var i int
                for i = p.ident; p.ns[i].tok != ftNewLine &&
                    p.ns[i].tok != token.CONTINUE &&
                    p.ns[i].tok != ftEnd; i++ {
                }
                a1 := p.ns[:p.ident]
                a2 := p.ns[p.ident+1 : i]
                b := append([]node{},
                    node{tok: ftNewLine, b: []byte("\n")},
                    p.ns[p.ident],
                    node{tok: token.CONTINUE, b: []byte("CONTINUE")},
                    node{tok: ftNewLine, b: []byte("\n")})
                c := p.ns[i:]
                p.ns = append(a1, append(a2, append(b, c...)...)...)
                return p.parseStmt()
            }
        }

        if v, ok := p.endLabelDo[labelName]; ok && 0 < v {
            stmts = append(stmts, p.addLabel(p.ns[p.ident].b))
            // if after END DO, then remove
            for i := p.ident; p.ns[i].tok != ftNewLine; i++ {
                p.ns[i].tok, p.ns[i].b = ftNewLine, []byte("\n")
            }

            // add END DO before that label
            var add []node
            for j := 0; j < v; j++ {
                add = append(add, []node{
                    {tok: ftNewLine, b: []byte("\n")},
                    {tok: ftEnd, b: []byte("END")},
                    {tok: ftNewLine, b: []byte("\n")},
                }...)
            }
            var comb []node
            comb = append(comb, p.ns[:p.ident-1]...)
            comb = append(comb, []node{
                {tok: ftNewLine, b: []byte("\n")},
                {tok: ftNewLine, b: []byte("\n")},
            }...)
            comb = append(comb, add...)
            comb = append(comb, []node{
                {tok: ftNewLine, b: []byte("\n")},
            }...)
            comb = append(comb, p.ns[p.ident-1:]...)
            p.ns = comb
            // remove do labels from map
            p.endLabelDo[labelName] = 0
            return
        }

        if p.ns[p.ident+1].tok == token.CONTINUE {
            stmts = append(stmts, p.addLabel(p.ns[p.ident].b))
            // replace CONTINUE to NEW_LINE
            p.ident++
            p.ns[p.ident].tok, p.ns[p.ident].b = ftNewLine, []byte("\n")
            return
        }

        stmts = append(stmts, p.addLabel(p.ns[p.ident].b))
        p.ident++
        return

    default:
        start := p.ident
        for ; p.ident < len(p.ns); p.ident++ {
            if p.ns[p.ident].tok == ftNewLine {
                break
            }
        }
        var isAssignStmt bool
        pos := start
        if p.ns[start].tok == token.IDENT {
            pos++
            if p.ns[pos].tok == token.LPAREN {
                counter := 0
                for ; pos < len(p.ns); pos++ {
                    switch p.ns[pos].tok {
                    case token.LPAREN:
                        counter++
                    case token.RPAREN:
                        counter--
                    }
                    if counter == 0 {
                        break
                    }
                }
                pos++
            }
            if p.ns[pos].tok == token.ASSIGN {
                isAssignStmt = true
            }
        }

        if isAssignStmt {
            // IMPLICIT initialization
            if v := p.ns[start]; v.tok == token.IDENT {
                _, ok := p.initVars.get(string(v.b))
                if !ok {
                    typ, ok := p.isImplicit(v.b[0])
                    if ok {
                        // add init
                        var inject []node
                        inject = append(inject, typ...)
                        for i := start; i < pos; i++ {
                            inject = append(inject, p.ns[i])
                        }
                        inject = append(inject, node{
                            tok: ftNewLine,
                            b:   []byte{'\n'},
                        })
                        p.ns = append(p.ns[:start], append(inject, p.ns[start:]...)...)
                        old := p.ident
                        p.ident = start
                        s := p.parseInit()
                        p.ident = old + len(inject)
                        if len(s) > 0 {
                            stmts = append(stmts, s...)
                        }
                        start += len(inject)
                        pos += len(inject)
                    }
                }
            }

            // add assign
            assign := goast.AssignStmt{
                Lhs: []goast.Expr{p.parseExpr(start, pos)},
                Tok: token.ASSIGN, // =
                Rhs: []goast.Expr{p.parseExpr(pos+1, p.ident)},
            }
            if f, ok := assign.Rhs[0].(*goast.CallExpr); ok {
                if !isIgnoreCall(f) {
                    assign.Rhs[0] = &goast.ParenExpr{X: &goast.StarExpr{X: assign.Rhs[0]}}
                }
            }
            stmts = append(stmts, &assign)
        } else {
            nodes := p.parseExpr(start, p.ident)
            stmts = append(stmts, &goast.ExprStmt{
                X: nodes,
            })
        }

        p.ident++
    }

    return
}

func (p *parser) addLabel(label []byte) (stmt goast.Stmt) {
    labelName := "Label" + string(label)
    p.allLabels[labelName] = true
    return &goast.LabeledStmt{
        Label: goast.NewIdent(labelName),
        Colon: 1,
        Stmt:  &goast.EmptyStmt{},
    }
}

func (p *parser) parseParamDecl() (fields []*goast.Field) {
    if p.ns[p.ident].tok != token.LPAREN {
        // Function or SUBROUTINE without arguments
        // Example:
        //  SubRoutine CLS
        return
    }
    p.expect(token.LPAREN)

    // Parameters
    p.ident++
    for ; p.ns[p.ident].tok != token.EOF; p.ident++ {
        var exit bool
        switch p.ns[p.ident].tok {
        case token.COMMA:
            // ignore
        case token.IDENT:
            id := strings.ToUpper(string(p.ns[p.ident].b))
            field := &goast.Field{
                Names: []*goast.Ident{goast.NewIdent(id)},
                Type:  goast.NewIdent("int"),
            }
            fields = append(fields, field)
        case token.RPAREN:
            p.ident--
            exit = true
        default:
            p.addError("Cannot parse parameter decl " + string(p.ns[p.ident].b))
            return
        }
        if exit {
            break
        }
    }

    p.ident++
    p.expect(token.RPAREN)

    p.ident++
    p.expect(ftNewLine)

    return
}

// Example:
// ( ( D ( I , J ) , J = 1 , 4 ) , I = 1 , 4 )
// =                             = = = = = = =
//
// Sign is change behavior ( KFIN ( 1 , J ) , J = - 40 , 40 )
func explodeFor(name []node) (out [][]node, ok bool) {
    // have loop
    if len(name) == 0 {
        return
    }
    if len(name) < 8 {
        return
    }
    if name[0].tok != token.LPAREN {
        return
    }
    {
        // replace to copy
        c := append([]node{}, name...)
        name = c
    }
    {
        // compess values
    again:
        for i := 2; i < len(name); i++ {
            if name[i].tok == token.INT &&
                name[i-2].tok != token.IDENT &&
                name[i-2].tok != token.INT {
                switch name[i-1].tok {
                case token.ADD: // +
                    // just remove token
                    name = append(name[:i-1], name[i:]...)
                    goto again
                case token.SUB: // -
                    // inject into value
                    name[i].b = append([]byte{'-'}, name[i].b...)
                    name = append(name[:i-1], name[i:]...)
                    goto again
                }
            }
        }
    }
    last := len(name)
    if name[last-1].tok != token.RPAREN {
        return
    }
    var Iend int
    if val, err := strconv.Atoi(string(name[last-2].b)); err != nil {
        return
    } else {
        Iend = val
    }
    var Istart int
    if val, err := strconv.Atoi(string(name[last-4].b)); err != nil {
        return
    } else {
        Istart = val
    }
    var Iname []byte
    Iname = name[last-6].b

    found := false
    for index := 1; index < last-7; index++ {
        if bytes.Equal(name[index].b, Iname) {
            found = true
        }
    }
    if !found {
        return
    }

    // generate names
    for i := Istart; i <= Iend; i++ {
        c := append([]node{}, name...)
        c = c[1 : last-7]
        for par := 0; par < len(c); par++ {
            if bytes.Equal(c[par].b, Iname) {
                c[par].b = []byte(strconv.Itoa(i))
                c[par].tok = token.INT
            }
        }
        // inject names
        out = append(out, c)
    }

    // recursive check again
    var summ [][]node
    for i := range out {
        if e, ok := explodeFor(out[i]); ok {
            summ = append(summ, e...)
            continue
        }
        summ = append(summ, out[i])
    }
    return summ, true
}

// Example:
// DATA GAM , GAMSQ , RGAMSQ / 4096.D0 , 16777216.D0 , 5.9604645D-8 /
//
// LOGICAL            ZSWAP( 4 )
// DATA               ZSWAP / .FALSE., .FALSE., .TRUE., .TRUE. /
//
// INTEGER            IPIVOT( 4, 4 )
// DATA               IPIVOT / 1, 2, 3, 4, 2, 1, 4, 3, 3, 4, 1, 2, 4, 3, 2, 1 /
//
// INTEGER            LOCL12( 4 ), LOCU21( 4 ),
// DATA               LOCU12 / 3, 4, 1, 2 / , LOCL21 / 2, 1, 4, 3 /
//
// DATA (M(I)  ,I= 1, 180) / ... /
// DATA (P(1,I),I= 1, 500) / ... /
//
// TODO:
//
// INTEGER            LV, IPW2
// PARAMETER          ( LV = 128 )
// INTEGER            J
// INTEGER            MM( LV, 4 )
//
// DATA (P(I,4),I= 1, 500) / ... /
// DATA ((D(I,J),J=1,4),I=1,4) / ... /

func (p *parser) parseData() (stmts []goast.Stmt) {
    p.expect(ftData)
    p.ident++

    // parse names and values
    var names [][]node
    names = append(names, []node{})
    var values [][]node
    counter := 0
    isNames := true
    for ; p.ident < len(p.ns); p.ident++ {
        if p.ns[p.ident].tok == ftNewLine {
            break
        }
        if p.ns[p.ident].tok == token.COMMENT {
            continue
        }
        if p.ns[p.ident].tok == token.QUO {
            if isNames {
                values = append(values, []node{})
            }
            isNames = !isNames
            continue
        }
        if p.ns[p.ident].tok == token.LPAREN {
            counter++
        }
        if p.ns[p.ident].tok == token.RPAREN {
            counter--
        }
        if p.ns[p.ident].tok == token.COMMA && counter == 0 {
            if isNames {
                names = append(names, []node{})
            } else {
                values = append(values, []node{})
            }
            continue
        }
        if isNames {
            names[len(names)-1] = append(names[len(names)-1], p.ns[p.ident])
        } else {
            values[len(values)-1] = append(values[len(values)-1], p.ns[p.ident])
        }
    }

    // Explode loops
    {
        var exp [][]node
        for i := range names {
            e, ok := explodeFor(names[i])
            if ok {
                exp = append(exp, e...)
                continue
            }
            exp = append(exp, names[i])
        }
        names = exp
    }

    // Example of names:
    // LL                       - value
    // LL                       - vector fully
    // LL                       - matrix fully
    // LL (1)                   - one value of vector
    // LL (1,1)                 - one value of matrix
    // (LL( J ), J = 1, 4 )     - one row of vector
    // (LL( 1, J ), J = 1, 4 )  - one row of matrix
    type tExpr struct {
        expr   goast.Expr
        isByte bool
    }

    for _, name := range names {
        _, ok := p.initVars.get(nodesToString(name[:1]))
        if !ok {
            p.initVars.add(nodesToString(name[:1]), goType{
                baseType: "float64",
            })
        }
    }

namesExplode:
    for i := range names {
        name := names[i]

        v, ok := p.initVars.get(nodesToString(name[:1]))
        if !ok {
            continue
        }
        lenArray := p.getArrayLen(v.name)

        // L(1,1) but size of len is 3
        exs := 0
        for i := range name {
            if name[i].tok == token.COMMA {
                exs++
            }
            if name[i].tok == token.LPAREN {
                exs++
            }
        }

        if lenArray == exs {
            continue
        }

        //          prefix|  | postfix
        // P    ->      P(|  |  )       -> P(1,1   )   , P(2,1   )   , P(1,2   )   , P(2,2   )   //
        // P(1) ->      P(|  |,1)       -> P(1,1 ,1)   , P(1,2 ,1)   , P(1,2 ,1)   , P(2,2 ,1)   //

        var prefix []node
        prefix = append([]node{name[0]}, node{tok: token.LPAREN, b: []byte("(")})

        var postfix []node
        postfix = append(postfix, node{tok: token.RPAREN, b: []byte(")")})
        if len(name) > 1 {
            postfix = append([]node{{tok: token.COMMA, b: []byte(",")}}, name[2:len(name)]...)
        }

        var inject [][]node
        switch lenArray - exs {
        case 1:
            var (
                s1, _    = v.typ.getMinLimit(lenArray - 1 - exs)
                size1, _ = p.getSize(v.name, lenArray-1-exs)
            )
            for i := 1; i <= size1; i++ {
                var sl []node
                sl = append(sl, prefix...)
                sl = append(sl,
                    node{tok: token.INT, b: []byte(strconv.Itoa(s1 + i - 1))},
                )
                sl = append(sl, postfix...)
                inject = append(inject, sl)
            }
        case 2:
            var (
                s1, _    = v.typ.getMinLimit(lenArray - 1 - exs - 1)
                size1, _ = p.getSize(v.name, lenArray-1-exs-1)
                s2, _    = v.typ.getMinLimit(lenArray - 1 - exs)
                size2, _ = p.getSize(v.name, lenArray-1-exs)
            )
            for j := 1; j <= size2; j++ {
                for i := 1; i <= size1; i++ {
                    var sl []node
                    sl = append(sl, prefix...)
                    sl = append(sl,
                        node{tok: token.INT, b: []byte(strconv.Itoa(s1 + i - 1))},
                        node{tok: token.COMMA, b: []byte(",")},
                        node{tok: token.INT, b: []byte(strconv.Itoa(s2 + j - 1))},
                    )
                    sl = append(sl, postfix...)
                    inject = append(inject, sl)
                }
            }
        case 3:
            var (
                s1, _    = v.typ.getMinLimit(lenArray - 1 - exs - 2)
                size1, _ = p.getSize(v.name, lenArray-1-exs-2)
                s2, _    = v.typ.getMinLimit(lenArray - 1 - exs - 1)
                size2, _ = p.getSize(v.name, lenArray-1-exs-1)
                s3, _    = v.typ.getMinLimit(lenArray - 1 - exs)
                size3, _ = p.getSize(v.name, lenArray-1-exs)
            )
            for k := 1; k <= size3; k++ {
                for j := 1; j <= size2; j++ {
                    for i := 1; i <= size1; i++ {
                        var sl []node
                        sl = append(sl, prefix...)
                        sl = append(sl,
                            node{tok: token.INT, b: []byte(strconv.Itoa(s1 + i - 1))},
                            node{tok: token.COMMA, b: []byte(",")},
                            node{tok: token.INT, b: []byte(strconv.Itoa(s2 + j - 1))},
                            node{tok: token.COMMA, b: []byte(",")},
                            node{tok: token.INT, b: []byte(strconv.Itoa(s3 + k - 1))},
                        )
                        sl = append(sl, postfix...)
                        inject = append(inject, sl)
                    }
                }
            }
        }

        names = append(names[:i], append(inject, names[i+1:]...)...)
        goto namesExplode
    }

    var nameExpr []tExpr
    for _, name := range names {
        v, _ := p.initVars.get(nodesToString(name[:1]))
        isByte := v.typ.getBaseType() == "byte"
        n := p.parseExprNodes(name)
        nameExpr = append(nameExpr, tExpr{
            expr:   n,
            isByte: isByte,
        })
    }

mul:
    for k := range values {
        // Example :
        // DATA R / 5*6 /
        // Equal:
        // DATA R / 6,6,6,6,6 /
        var haveStar bool
        var starPos int
        for i, vi := range values[k] {
            if vi.tok == token.MUL {
                haveStar = true
                starPos = i
                break
            }
        }

        if !haveStar {
            continue
        }

        amount, _ := strconv.Atoi(string(values[k][starPos-1].b))
        var inject [][]node
        for i := 0; i < amount; i++ {
            inject = append(inject, values[k][starPos+1:])
        }
        values = append(values[:k], append(inject, values[k+1:]...)...)
        goto mul
    }

    for k := range values {
        for j := range values[k] {
            // [[STRING, `"123456"`, {898 24}]]
            if values[k][j].tok != token.STRING {
                continue
            }
            if len(values[k][j].b) < 4 {
                continue
            }

            var inject [][]node
            for r := range values[k][j].b {
                if r == 0 || r == len(values[k][j].b)-1 {
                    continue
                }
                inject = append(inject, []node{{
                    tok: token.STRING,
                    b:   []byte{'\'', values[k][j].b[r], '\''},
                }})
            }
            values = append(values[:k], append(inject, values[k+1:]...)...)

            goto mul
        }
    }

    if len(nameExpr) != len(values) {
        var str string
        for i := range names {
            str += fmt.Sprintln(">>", nodesToString(names[i]))
            v, ok := p.initVars.get(nodesToString(names[i]))
            if ok {
                str += fmt.Sprintln("1) ", v.name)
                str += fmt.Sprintln("2) ", v.typ)
                str += fmt.Sprintln("3) ", v.typ.baseType)
                str += fmt.Sprintln("4) ", v.typ.getBaseType())
                str += fmt.Sprintln("5) ", v.typ.arrayNode)
            }
            if i > 10 {
                str += fmt.Sprintf("and other %d names ...", len(names)-i)
                break
            }
        }
        str += fmt.Sprintln(" amount nameExpr is ", len(nameExpr))
        for i := range nameExpr {
            str += fmt.Sprintln("nameExpr[", i, "] =", nameExpr[i])
            if i > 10 {
                str += fmt.Sprintf("and other %d values ...", len(nameExpr)-i)
                break
            }
        }
        str += fmt.Sprintln(" amount values is ", len(values))
        for i := range values {
            str += fmt.Sprintln("value[", i, "]=", nodesToString(values[i]))
            if i > 10 {
                str += fmt.Sprintf("and other %d values ...", len(values)-i)
                break
            }
        }
        panic(fmt.Errorf("Size is not same %d!=%d\n%v",
            len(nameExpr), len(values), str))
    }

    if len(nameExpr) > 0 {
        var assign goast.AssignStmt
        assign.Tok = token.ASSIGN // =

        for i := range nameExpr {
            if nameExpr[i].isByte {
                e := p.parseExprNodes(values[i])
                e.(*goast.BasicLit).Kind = token.CHAR
                e.(*goast.BasicLit).Value = fmt.Sprintf("'%c'", e.(*goast.BasicLit).Value[1])
                assign.Lhs = append(assign.Lhs, nameExpr[i].expr)
                assign.Rhs = append(assign.Rhs, e)
                continue
            }
            assign.Lhs = append(assign.Lhs, nameExpr[i].expr)
            assign.Rhs = append(assign.Rhs, p.parseExprNodes(values[i]))
        }

        stmts = append(stmts, &assign)
    }

    return
}

// Examples:
//  GO TO 30
//  GO TO ( 40, 80 )IEXC
//  GO TO next,(30, 50, 70, 90, 110)
func (p *parser) parseGoto() (stmts []goast.Stmt) {
    p.expect(token.GOTO)

    p.ident++
    if p.ns[p.ident].tok != token.LPAREN {
        //  GO TO 30
        p.foundLabels["Label"+string(p.ns[p.ident].b)] = true
        stmts = append(stmts, &goast.BranchStmt{
            Tok:   token.GOTO,
            Label: goast.NewIdent("Label" + string(p.ns[p.ident].b)),
        })
        p.ident++
        return
    }
    //  GO TO ( 40, 80, 100 )IEXC
    //  GO TO ( 40 )IEXC

    // parse labels
    p.expect(token.LPAREN)
    var labelNames []string
    for ; p.ident < len(p.ns); p.ident++ {
        var out bool
        switch p.ns[p.ident].tok {
        case token.LPAREN:
            // do nothing
        case token.RPAREN:
            out = true
        case token.COMMA:
            // do nothing
        default:
            labelNames = append(labelNames, string(p.ns[p.ident].b))
            p.foundLabels["Label"+string(p.ns[p.ident].b)] = true
        }
        if out {
            break
        }
    }
    p.expect(token.RPAREN)
    p.ident++

    // ignore COMMA
    if p.ns[p.ident].tok == token.COMMA {
        p.ident++
    }

    if len(labelNames) == 0 {
        panic("Not acceptable amount of labels in GOTO")
    }

    // get expr
    st := p.ident
    for ; p.ident < len(p.ns) && p.ns[p.ident].tok != ftNewLine; p.ident++ {
    }
    // generate Go code
    var sw goast.SwitchStmt
    sw.Tag = p.parseExpr(st, p.ident)
    sw.Body = &goast.BlockStmt{}
    for i := 0; i < len(labelNames); i++ {
        sw.Body.List = append(sw.Body.List, &goast.CaseClause{
            List: []goast.Expr{goast.NewIdent(strconv.Itoa(i + 1))},
            Body: []goast.Stmt{&goast.BranchStmt{
                Tok:   token.GOTO,
                Label: goast.NewIdent("Label" + labelNames[i]),
            }},
        })
    }

    stmts = append(stmts, &sw)

    return
}

//  PARAMETER ( ONE = ( 1.0E+0 , 0.0E+0 )  , ZERO = 0.0E+0 )
//  PARAMETER ( LV = 2 )
func (p *parser) parseParameter() (stmts []goast.Stmt) {
    start := p.ident
    p.expect(ftParameter)
    p.ident++
    p.expect(token.LPAREN)
    // parse values
    var names [][]node
    counter := 1
    p.ident++
    for ; p.ident < len(p.ns); p.ident++ {
        if p.ns[p.ident].tok == token.LPAREN {
            counter++
        }
        if p.ns[p.ident].tok == token.RPAREN {
            counter--
        }
        if p.ns[p.ident].tok == token.RPAREN && counter == 0 {
            break
        }
        if p.ns[p.ident].tok == token.COMMA && counter == 1 {
            names = append(names, []node{})
            continue
        }
        if len(names) == 0 {
            names = append(names, []node{})
        }
        names[len(names)-1] = append(names[len(names)-1], p.ns[p.ident])
    }

    // split to name and value
    for _, val := range names {
        for i := 0; i < len(val); i++ {
            if val[i].tok == token.ASSIGN {
                // add parameters in parser
                p.constants[nodesToString(val[:i])] = val[i+1:]
            }
        }
    }

    p.expect(token.RPAREN)
    p.ident++

    p.ident = start

    p.ns[p.ident].tok = ftNewLine
    p.ident++
    p.ns[p.ident].tok = ftNewLine
    counter = 1
    for ; p.ident < len(p.ns); p.ident++ {
        if p.ns[p.ident].tok == token.LPAREN {
            counter++
        }
        if p.ns[p.ident].tok == token.RPAREN {
            counter--
        }
        if p.ns[p.ident].tok == token.RPAREN && counter == 0 {
            p.ns[p.ident].tok = ftNewLine
            p.ns[p.ident].b = []byte("\n")
            break
        }
        if p.ns[p.ident].tok == token.COMMA && counter == 1 {
            p.ns[p.ident].tok = ftNewLine
            p.ns[p.ident].b = []byte("\n")
            continue
        }
    }
    p.ident = start
    return
}

func (p *parser) parseAssign() (stmts []goast.Stmt) {
    p.expect(ftAssign)
    p.ident++

    statement := p.ident
    p.ident++

    // ignore TO
    p.ident++

    intVar := p.ident
    p.ident++
    stmts = append(stmts, &goast.ExprStmt{
        X: goast.NewIdent(fmt.Sprintf("// ASSIGN %v TO %v", string(p.ns[statement].b), string(p.ns[intVar].b))),
    }, &goast.AssignStmt{
        Lhs: []goast.Expr{p.parseExpr(intVar, intVar+1)},
        Tok: token.ASSIGN,
        Rhs: []goast.Expr{p.parseExpr(statement, statement+1)},
    }, &goast.AssignStmt{
        Lhs: []goast.Expr{goast.NewIdent("_")},
        Tok: token.ASSIGN,
        Rhs: []goast.Expr{p.parseExpr(intVar, intVar+1)},
    })

    return
}

// Examples:
//    COMMON/PDAT/LOC(3), T(1)
//    COMMON/      /B(160000)
// Implementation:
// var COMMON MEMORY
// type MEMORY struct {
//        PDAT struct {
//            LOC [3]int
//            T   [1]float64
//        }
// }
//
//    COMMON A,B
//
// type MEMORY struct {
//        A [2]int
//        B [1]float64
// }
//
func (p *parser) parseCommon() (stmts []goast.Stmt) {
    p.expect(ftCommon)
    p.ident++

    var blockName string = "ALL"
    if p.ns[p.ident].tok == token.QUO { // /
        // find block name
        p.ident++
        if p.ns[p.ident].tok != token.QUO {
            blockName = string(p.ns[p.ident].b)
            p.ident++
        }
        p.expect(token.QUO) // /
        p.ident++
    }

    // variable names without type
    var names []string
    newIdent := true
    isopen := false
    for ; ; p.ident++ {
        exit := false
        switch v := p.ns[p.ident]; v.tok {
        case token.COMMA:
            if isopen == false {
                newIdent = true
            } else {
                names[len(names)-1] += string(v.b)
            }
            continue
        case ftNewLine:
            exit = true
        default:
            if v.tok == token.LPAREN {
                isopen = true
            }
            if v.tok == token.RPAREN {
                isopen = false
            }
            if newIdent {
                newIdent = false
                names = append(names, string(v.b))
            } else {
                names[len(names)-1] += string(v.b)
            }
        }
        if exit {
            break
        }
    }

    p.expect(ftNewLine)

    // put block name in parser
    var variables []varInitialization
    for i := range names {
        name := names[i]
        var addition []node
        if index := strings.Index(names[i], "("); index > 0 {
            addition = scan([]byte(name[index:]))
            name = name[:index]
        }
        var typ goType
        var typNode node
        if i == 0 {
            typNode = node{tok: ftInteger, b: []byte("INTEGER")}
        } else {
            typNode = node{tok: ftReal, b: []byte("REAL")}
        }
        typ = parseType(append([]node{typNode}, addition...))

        if v, ok := p.initVars.get(name); ok {
            typ = v.typ
        }

        variables = append(variables, varInitialization{name: name, typ: typ})
    }
    p.Common.addBlockName(blockName, variables)

    // generate stmts
    // {{ .name }} = COMMON.{{ .blockName }}.{{ name }}
    for i := range names {

        // if variable is not initialized
        name := names[i]
        if index := strings.Index(names[i], "("); index > 0 {
            name = name[:index]
        }
        var inject []node
        n := scan([]byte(names[i]))
        if _, ok := p.initVars.get(name); !ok {
            // from:
            //    COMMON LOC(3), T(1)
            // to:
            //    INTEGER LOC(3)
            //    REAL T(1)
            //    COMMON LOC(3), T(1)

            // INTEGER name
            inject = append(inject, node{tok: ftNewLine, b: []byte("\n")})
            if i == 0 {
                inject = append(inject, node{tok: ftInteger, b: []byte("INTEGER")})
            } else {
                inject = append(inject, node{tok: ftReal, b: []byte("REAL")})
            }
            inject = append(inject, n...)
            inject = append(inject, node{tok: ftNewLine, b: []byte("\n")})
        }

        // INTEGER COMMON.blockName.name
        n = scan([]byte(names[i]))
        inject = append(inject, node{tok: ftNewLine, b: []byte("\n")})
        if i == 0 {
            inject = append(inject, node{tok: ftInteger, b: []byte("INTEGER")})
        } else {
            inject = append(inject, node{tok: ftReal, b: []byte("REAL")})
        }
        n[0].b = append([]byte("COMMON."+blockName+"."), n[0].b...)
        n[0].tok = token.IDENT
        inject = append(inject, n...)
        inject = append(inject, node{tok: ftNewLine, b: []byte("\n")})

        p.ns = append(p.ns[:p.ident], append(inject, p.ns[p.ident:]...)...)

        // name = COMMON.blockName.name
        stmts = append(stmts, &goast.AssignStmt{
            Lhs: []goast.Expr{goast.NewIdent(name)},
            Tok: token.ASSIGN, // =
            Rhs: []goast.Expr{goast.NewIdent("COMMON." + blockName + "." + name)},
        })
    }

    return
}