func (v *Context) parseFile(path string, module *ast.Module) { // Read sourcefile, err := lexer.NewSourcefile(path) 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 := ast.NewModuleName(dep) v.modulesToRead = append(v.modulesToRead, depname) v.depGraph.AddDependency(module.Name, depname) if _, _, err := v.findModuleDir(depname.ToPath()); err != nil { log.Errorln("main", "%s [%s:%d:%d] Couldn't find module `%s`", util.Red("error:"), dep.Where().Filename, dep.Where().StartLine, dep.Where().EndLine, depname.String()) log.Errorln("main", "%s", sourcefile.MarkSpan(dep.Where())) os.Exit(1) } } }
func build(files []string, outputFile string, cg string, outputType LLVMCodegen.OutputType, optLevel int) { constructedModules, _ := parseFiles(files) // resolve log.Timed("resolve phase", "", func() { for _, module := range constructedModules { res := &parser.Resolver{Module: module} vis := parser.NewASTVisitor(res) vis.VisitModule(module) } }) // type inference log.Timed("inference phase", "", func() { for _, module := range constructedModules { inf := &parser.TypeInferer{Module: module} inf.Infer() // Dump AST log.Debugln("main", "AST of module `%s`:", module.Name) for _, node := range module.Nodes { log.Debugln("main", "%s", node.String()) } log.Debugln("main", "") } }) // semantic analysis log.Timed("semantic analysis phase", "", func() { for _, module := range constructedModules { sem := semantic.NewSemanticAnalyzer(module, *buildOwnership, *ignoreUnused) vis := parser.NewASTVisitor(sem) vis.VisitModule(module) sem.Finalize() } }) // codegen if cg != "none" { var gen codegen.Codegen switch cg { case "llvm": gen = &LLVMCodegen.Codegen{ OutputName: outputFile, OutputType: outputType, OptLevel: optLevel, } default: log.Error("main", util.Red("error: ")+"Invalid backend choice `"+cg+"`") os.Exit(1) } log.Timed("codegen phase", "", func() { gen.Generate(constructedModules) }) } }
func build(files []string, outputFile string, cg string, ccArgs []string, outputType LLVMCodegen.OutputType) { constructedModules, modules := parseFiles(files) // resolve timed("resolve phase", func() { // TODO: We're looping over a map, the order we get is thus random for _, module := range modules { res := &parser.Resolver{Module: module} res.Resolve(modules) } }) // semantic analysis timed("semantic analysis phase", func() { // TODO: We're looping over a map, the order we get is thus random for _, module := range modules { sem := &parser.SemanticAnalyzer{Module: module} sem.Analyze(modules) } }) // codegen if cg != "none" { var gen codegen.Codegen switch cg { case "llvm": gen = &LLVMCodegen.Codegen{ OutputName: outputFile, CompilerArgs: ccArgs, OutputType: outputType, } default: log.Error("main", util.Red("error: ")+"Invalid backend choice `"+cg+"`") os.Exit(1) } timed("codegen phase", func() { gen.Generate(constructedModules, modules) }) } }
func build(files []string, outputFile string, cg string, outputType LLVMCodegen.OutputType, optLevel int) { constructedModules, moduleLookup := parseFiles(files) // resolve hasMainFunc := false log.Timed("resolve phase", "", func() { for _, module := range constructedModules { parser.Resolve(module, moduleLookup) // Use module scope to check for main function mainIdent := module.ModScope.GetIdent(parser.UnresolvedName{Name: "main"}) if mainIdent != nil && mainIdent.Type == parser.IDENT_FUNCTION && mainIdent.Public { hasMainFunc = true } } }) // 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) } // type inference log.Timed("inference phase", "", func() { for _, module := range constructedModules { for _, submod := range module.Parts { inf := &parser.TypeInferer{Submodule: submod} inf.Infer() // Dump AST log.Debugln("main", "AST of submodule `%s/%s`:", module.Name, submod.File.Name) for _, node := range submod.Nodes { log.Debugln("main", "%s", node.String()) } log.Debugln("main", "") } } }) // semantic analysis log.Timed("semantic analysis phase", "", func() { for _, module := range constructedModules { for _, submod := range module.Parts { sem := semantic.NewSemanticAnalyzer(submod, *buildOwnership, *ignoreUnused) vis := parser.NewASTVisitor(sem) vis.VisitSubmodule(submod) sem.Finalize() } } }) // codegen if cg != "none" { var gen codegen.Codegen switch cg { case "llvm": gen = &LLVMCodegen.Codegen{ OutputName: outputFile, OutputType: outputType, OptLevel: optLevel, } default: log.Error("main", util.Red("error: ")+"Invalid backend choice `"+cg+"`") os.Exit(1) } log.Timed("codegen phase", "", func() { gen.Generate(constructedModules) }) } }
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 }
func (v *Context) parseFiles() { if strings.HasSuffix(v.Input, ".ark") { // Handle the special case of a single .ark file modname := &ast.ModuleName{Parts: []string{"__main"}} module := &ast.Module{ Name: modname, Dirpath: "", } v.moduleLookup.Create(modname).Module = module v.parseFile(v.Input, module) v.modules = append(v.modules, module) } else { if strings.ContainsAny(v.Input, `\/. `) { setupErr("Invalid module name: %s", v.Input) } modname := &ast.ModuleName{Parts: strings.Split(v.Input, "::")} v.modulesToRead = append(v.modulesToRead, modname) } log.Timed("read/lex/parse phase", "", func() { for i := 0; i < len(v.modulesToRead); i++ { modname := v. modulesToRead[i] // Skip already loaded modules if _, err := v.moduleLookup.Get(modname); err == nil { continue } fi, dirpath, err := v.findModuleDir(modname.ToPath()) if err != nil { setupErr("Couldn't find module `%s`: %s", modname, err) } if !fi.IsDir() { setupErr("Expected path `%s` to be directory, was file.", dirpath) } module := &ast.Module{ Name: modname, Dirpath: dirpath, } v.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()) v.parseFile(actualFile, module) } v.modules = append(v.modules, module) } }) // Check for cyclic dependencies (in modules) log.Timed("cyclic dependency check", "", func() { errs := v.depGraph.DetectCycles() if len(errs) > 0 { log.Error("main", "%s: Encountered cyclic dependency between: ", util.Bold(util.Red("error"))) for _, cycle := range errs { log.Error("main", "%s", cycle) } log.Errorln("main", "") os.Exit(util.EXIT_FAILURE_SETUP) } }) // construction log.Timed("construction phase", "", func() { for _, module := range v.modules { ast.Construct(module, v.moduleLookup) } }) }
func (v *Context) Build(output string, outputType codegen.OutputType, usedCodegen string, optLevel int) { // Start by loading the runtime runtimeModule := LoadRuntime() // Parse the passed files v.parseFiles() // resolve hasMainFunc := false log.Timed("resolve phase", "", func() { for _, module := range v.modules { ast.Resolve(module, v.moduleLookup) // Use module scope to check for main function mainIdent := module.ModScope.GetIdent(ast.UnresolvedName{Name: "main"}) if mainIdent != nil && mainIdent.Type == ast.IDENT_FUNCTION && mainIdent.Public { hasMainFunc = true } } }) // 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) } // type inference log.Timed("inference phase", "", func() { for _, module := range v.modules { for _, submod := range module.Parts { ast.Infer(submod) // Dump AST log.Debugln("main", "AST of submodule `%s/%s`:", module.Name, submod.File.Name) for _, node := range submod.Nodes { log.Debugln("main", "%s", node.String()) } log.Debugln("main", "") } } }) // semantic analysis log.Timed("semantic analysis phase", "", func() { for _, module := range v.modules { semantic.SemCheck(module, *ignoreUnused) } }) // codegen if usedCodegen != "none" { var gen codegen.Codegen switch usedCodegen { case "llvm": gen = &LLVMCodegen.Codegen{ OutputName: output, OutputType: outputType, OptLevel: optLevel, } default: log.Error("main", util.Red("error: ")+"Invalid backend choice `"+usedCodegen+"`") os.Exit(1) } log.Timed("codegen phase", "", func() { mods := v.modules if runtimeModule != nil { mods = append(mods, runtimeModule) } gen.Generate(mods) }) } }