func parseFiles(inputs []string) ([]*parser.Module, *parser.ModuleLookup) { if len(inputs) != 1 { setupErr("Please specify only one file or module to build") } var modulesToRead []*parser.ModuleName var modules []*parser.Module moduleLookup := parser.NewModuleLookup("") depGraph := parser.NewDependencyGraph() input := inputs[0] if strings.HasSuffix(input, ".ark") { // Handle the special case of a single .ark file modname := &parser.ModuleName{Parts: []string{"__main"}} module := &parser.Module{ Name: modname, Dirpath: "", } moduleLookup.Create(modname).Module = module // Read sourcefile, err := lexer.NewSourcefile(input) if err != nil { setupErr("%s", err.Error()) } // Lex sourcefile.Tokens = lexer.Lex(sourcefile) // Parse parsedFile, deps := parser.Parse(sourcefile) module.Trees = append(module.Trees, parsedFile) // Add dependencies to parse array for _, dep := range deps { depname := parser.NewModuleName(dep) modulesToRead = append(modulesToRead, depname) depGraph.AddDependency(modname, depname) } modules = append(modules, module) } else { if strings.ContainsAny(input, `\/. `) { setupErr("Invalid module name: %s", input) } modname := &parser.ModuleName{Parts: strings.Split(input, "::")} modulesToRead = append(modulesToRead, modname) } log.Timed("read/lex/parse phase", "", func() { for i := 0; i < len(modulesToRead); i++ { modname := modulesToRead[i] // Skip already loaded modules if _, err := moduleLookup.Get(modname); err == nil { continue } fi, dirpath := findModuleDir(*buildSearchpaths, modname.ToPath()) if fi == nil { setupErr("Couldn't find module `%s`", modname) } if !fi.IsDir() { setupErr("Expected path `%s` to be directory, was file.", dirpath) } module := &parser.Module{ Name: modname, Dirpath: dirpath, } moduleLookup.Create(modname).Module = module // Check module children childFiles, err := ioutil.ReadDir(dirpath) if err != nil { setupErr("%s", err.Error()) } for _, childFile := range childFiles { if strings.HasPrefix(childFile.Name(), ".") || !strings.HasSuffix(childFile.Name(), ".ark") { continue } actualFile := filepath.Join(dirpath, childFile.Name()) // Read sourcefile, err := lexer.NewSourcefile(actualFile) if err != nil { setupErr("%s", err.Error()) } // Lex sourcefile.Tokens = lexer.Lex(sourcefile) // Parse parsedFile, deps := parser.Parse(sourcefile) module.Trees = append(module.Trees, parsedFile) // Add dependencies to parse array for _, dep := range deps { depname := parser.NewModuleName(dep) modulesToRead = append(modulesToRead, depname) depGraph.AddDependency(modname, depname) } } modules = append(modules, module) } }) // Check for cyclic dependencies (in modules) log.Timed("cyclic dependency check", "", func() { errs := depGraph.DetectCycles() if len(errs) > 0 { log.Errorln("main", "error: Encountered cyclic dependecies:") for _, cycle := range errs { log.Errorln("main", "%s", cycle) } os.Exit(util.EXIT_FAILURE_SETUP) } }) // construction log.Timed("construction phase", "", func() { for _, module := range modules { parser.Construct(module, moduleLookup) } }) return modules, moduleLookup }
func parseFiles(inputs []string) ([]*parser.Module, *parser.ModuleLookup) { var modulesToRead []*parser.ModuleName for _, input := range inputs { if strings.ContainsAny(input, `\/. `) { setupErr("Invalid module name: %s", input) } modname := &parser.ModuleName{Parts: strings.Split(input, "::")} modulesToRead = append(modulesToRead, modname) } var parsedFiles []*parser.ParseTree moduleLookup := parser.NewModuleLookup("") depGraph := parser.NewDependencyGraph() log.Timed("read/lex/parse phase", "", func() { for i := 0; i < len(modulesToRead); i++ { modname := modulesToRead[i] actualFile := filepath.Join(*buildBasedir, modname.ToPath()) fi, err := os.Stat(actualFile + ".ark") shouldBeDir := false if os.IsNotExist(err) { fi, err = os.Stat(actualFile) shouldBeDir = true } if err != nil { setupErr("%s", err.Error()) } if !fi.IsDir() && shouldBeDir { setupErr("Expected file `%s` to be directory, was file.", actualFile) } // Check lookup ll := moduleLookup.Create(modname) if ll.Tree == nil { if shouldBeDir { // Setup dummy parse tree ll.Tree = &parser.ParseTree{} ll.Tree.Name = modname // Setup dummy module ll.Module = &parser.Module{} ll.Module.Path = actualFile ll.Module.Name = modname ll.Module.GlobalScope = parser.NewGlobalScope() // Check module children childFiles, err := ioutil.ReadDir(actualFile) if err != nil { setupErr("%s", err.Error()) } for _, childFile := range childFiles { if strings.HasPrefix(childFile.Name(), ".") { continue } if childFile.IsDir() || strings.HasSuffix(childFile.Name(), ".ark") { childmodname := parser.JoinModuleName(modname, strings.TrimSuffix(childFile.Name(), ".ark")) modulesToRead = append(modulesToRead, childmodname) ll.Module.GlobalScope.UsedModules[childmodname.Last()] = moduleLookup.Create(childmodname) } } } else { // Read sourcefile, err := lexer.NewSourcefile(actualFile + ".ark") if err != nil { setupErr("%s", err.Error()) } // Lex sourcefile.Tokens = lexer.Lex(sourcefile) // Parse parsedFile, deps := parser.Parse(sourcefile) parsedFile.Name = modname parsedFiles = append(parsedFiles, parsedFile) ll.Tree = parsedFile // Add dependencies to parse array for _, dep := range deps { depname := parser.NewModuleName(dep) modulesToRead = append(modulesToRead, depname) depGraph.AddDependency(modname, depname) } } } } }) // Check for cyclic dependencies (in modules) log.Timed("cyclic dependency check", "", func() { errs := depGraph.DetectCycles() if len(errs) > 0 { log.Errorln("main", "error: Encountered cyclic dependecies:") for _, cycle := range errs { log.Errorln("main", "%s", cycle) } os.Exit(util.EXIT_FAILURE_SETUP) } }) hasMainFunc := false // construction var constructedModules []*parser.Module log.Timed("construction phase", "", func() { for _, file := range parsedFiles { mod := parser.Construct(file, moduleLookup) constructedModules = append(constructedModules, mod) // not terribly efficient, but it's best // to catch out earlier on if we have // a main function or not rather than // figuring out at the codegen phase?? // maybe?? // TODO FIXME MAKEPRETTY for _, node := range mod.Nodes { switch node := node.(type) { case *parser.FunctionDecl: if node.Function.Name == "main" { hasMainFunc = true break } default: } } } }) // and here we check if we should // bother continuing any further... if !hasMainFunc { log.Error("main", util.Red("error: ")+"main function not found\n") os.Exit(1) } return constructedModules, moduleLookup }