func (v *RecursiveDefinitionCheck) Visit(s *SemanticAnalyzer, n parser.Node) { var typ parser.Type if typeDecl, ok := n.(*parser.TypeDecl); ok { actualType := typeDecl.NamedType.ActualType() switch actualType.(type) { case *parser.EnumType: typ = actualType.(*parser.EnumType) case *parser.StructType: typ = actualType.(*parser.StructType) // TODO: Check tuple types once we add named types for everything default: return } } if ok, path := isTypeRecursive(typ); ok { s.Err(n, "Encountered recursive type definition") log.Errorln("semantic", "Path taken:") for _, typ := range path { log.Error("semantic", typ.TypeName()) log.Error("semantic", " <- ") } log.Error("semantic", "%s\n\n", typ.TypeName()) } }
func (v *NameMap) TypeOfNameNode(name *NameNode) NodeType { mod := v typ := NODE_MODULE for _, modName := range name.Modules { if typ == NODE_MODULE { typ = mod.typeOf(modName) if typ == NODE_MODULE { mod = mod.module(modName) if mod == nil { return NODE_UNKOWN } } } else { startPos := modName.Where.Start() log.Errorln("constructor", "[%s:%d:%d] Invalid use of `::`. `%s` is not a module", startPos.Filename, startPos.Line, startPos.Char, modName.Value) log.Error("constructor", v.tree.Source.MarkSpan(modName.Where)) } } if typ == NODE_ENUM { return NODE_ENUM_MEMBER } else if typ == NODE_STRUCT { return NODE_STRUCT_STATIC } typ = mod.typeOf(name.Name) if typ == NODE_UNKOWN { startPos := name.Name.Where.Start() log.Errorln("constructor", "[%s:%d:%d] Undeclared name `%s`", startPos.Filename, startPos.Line, startPos.Char, name.Name.Value) log.Error("constructor", v.tree.Source.MarkSpan(name.Name.Where)) } return typ }
func (v *SemanticAnalyzer) err(thing Locatable, err string, stuff ...interface{}) { pos := thing.Pos() log.Error("semantic", util.TEXT_RED+util.TEXT_BOLD+"Semantic error:"+util.TEXT_RESET+" [%s:%d:%d] %s\n", pos.Filename, pos.Line, pos.Char, fmt.Sprintf(err, stuff...)) log.Error("semantic", v.Module.File.MarkPos(pos)) v.shouldExit = true }
func (v *Resolver) err(thing Locatable, err string, stuff ...interface{}) { pos := thing.Pos() log.Error("resolve", util.TEXT_RED+util.TEXT_BOLD+"Resolve error:"+util.TEXT_RESET+" [%s:%d:%d] %s\n", pos.Filename, pos.Line, pos.Char, fmt.Sprintf(err, stuff...)) log.Error("resolve", v.Module.File.MarkPos(pos)) os.Exit(util.EXIT_FAILURE_SEMANTIC) }
func (v *RecursiveDefinitionCheck) Visit(s *SemanticAnalyzer, n ast.Node) { if typeDecl, ok := n.(*ast.TypeDecl); ok { typ := typeDecl.NamedType if ok, path := isTypeRecursive(typ); ok { s.Err(n, "Encountered recursive type definition") log.Errorln("semantic", "Path taken:") for _, typ := range path { log.Error("semantic", typ.TypeName()) log.Error("semantic", " <- ") } log.Error("semantic", "%s\n\n", typ.TypeName()) } } }
func (v *Scope) err(err string, stuff ...interface{}) { // TODO: which log tag // TODO: These errors are unacceptably shitty log.Error("parser", util.TEXT_RED+util.TEXT_BOLD+"error:"+util.TEXT_RESET+" %s\n", fmt.Sprintf(err, stuff...)) os.Exit(util.EXIT_FAILURE_PARSE) }
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 (v *lexer) errPos(pos Position, err string, stuff ...interface{}) { log.Errorln("lexer", util.TEXT_RED+util.TEXT_BOLD+"error:"+util.TEXT_RESET+" [%s:%d:%d] %s", pos.Filename, pos.Line, pos.Char, fmt.Sprintf(err, stuff...)) log.Error("lexer", v.input.MarkPos(pos)) os.Exit(1) }
func (v *NameMap) Module(name LocatedString) *NameMap { mod := v.module(name) if mod == nil { startPos := name.Where.Start() log.Errorln("constructor", "[%s:%d:%d] Unknown module `%s`", startPos.Filename, startPos.Line, startPos.Char, name.Value) log.Error("constructor", v.tree.Source.MarkSpan(name.Where)) } return mod }
func (v *NameMap) TypeOf(name LocatedString) NodeType { typ := v.typeOf(name) if typ == NODE_UNKOWN { startPos := name.Where.Start() log.Errorln("constructor", "[%s:%d:%d] Undeclared name `%s`", startPos.Filename, startPos.Line, startPos.Char, name.Value) log.Error("constructor", v.tree.Source.MarkSpan(name.Where)) } return typ }
func (v *Constructor) errSpan(pos lexer.Span, err string, stuff ...interface{}) { log.Errorln("constructor", util.TEXT_RED+util.TEXT_BOLD+"error:"+util.TEXT_RESET+" [%s:%d:%d] %s", pos.Filename, pos.StartLine, pos.StartChar, fmt.Sprintf(err, stuff...)) log.Error("constructor", v.curTree.Source.MarkSpan(pos)) os.Exit(util.EXIT_FAILURE_CONSTRUCTOR) }
func (v *Constructor) errPos(pos lexer.Position, err string, stuff ...interface{}) { log.Errorln("constructor", util.TEXT_RED+util.TEXT_BOLD+"Constructor error:"+util.TEXT_RESET+" [%s:%d:%d] %s", pos.Filename, pos.Line, pos.Char, fmt.Sprintf(err, stuff...)) log.Error("constructor", v.tree.Source.MarkPos(pos)) os.Exit(util.EXIT_FAILURE_CONSTRUCTOR) }
func (v *parser) errPosSpecific(pos lexer.Position, err string, stuff ...interface{}) { v.dumpRules() log.Errorln("parser", util.TEXT_RED+util.TEXT_BOLD+"error:"+util.TEXT_RESET+" [%s:%d:%d] %s", pos.Filename, pos.Line, pos.Char, fmt.Sprintf(err, stuff...)) log.Error("parser", v.input.MarkPos(pos)) os.Exit(util.EXIT_FAILURE_PARSE) }
func (v *parser) errTokenSpecific(tok *lexer.Token, err string, stuff ...interface{}) { v.dumpRules() log.Errorln("parser", util.TEXT_RED+util.TEXT_BOLD+"error:"+util.TEXT_RESET+" [%s:%d:%d] %s", tok.Where.Filename, tok.Where.StartLine, tok.Where.StartChar, fmt.Sprintf(err, stuff...)) log.Error("parser", v.input.MarkSpan(tok.Where)) os.Exit(util.EXIT_FAILURE_PARSE) }
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 (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) }) } }
func (v *Scope) err(err string, stuff ...interface{}) { // TODO: which log tag log.Error("parser", util.TEXT_RED+util.TEXT_BOLD+"error:"+util.TEXT_RESET+" %s\n", fmt.Sprintf(err, stuff...)) os.Exit(util.EXIT_FAILURE_CODEGEN) }
func setupErr(err string, stuff ...interface{}) { log.Error("main", util.TEXT_RED+util.TEXT_BOLD+"error:"+util.TEXT_RESET+" %s\n", fmt.Sprintf(err, stuff...)) os.Exit(util.EXIT_FAILURE_SETUP) }
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 MapNames(nodes []ParseNode, tree *ParseTree, modules map[string]*ParseTree, parent *NameMap) *NameMap { nameMap := &NameMap{} nameMap.parent = parent nameMap.tree = tree nameMap.types = make(map[string]NodeType) nameMap.modules = make(map[string]*NameMap) previousLocation := make(map[string]lexer.Span) shouldExit := false var cModule *NameMap if parent == nil { for i := 0; i < len(_PrimitiveType_index); i++ { typ := PrimitiveType(i) name := typ.TypeName() nameMap.types[name] = NODE_PRIMITIVE previousLocation[name] = lexer.Span{Filename: "_builtin"} } cModule = &NameMap{ types: make(map[string]NodeType), modules: make(map[string]*NameMap), } cModule.types["uint"] = NODE_TYPE cModule.types["int"] = NODE_TYPE nameMap.types["C"] = NODE_MODULE nameMap.modules["C"] = cModule } else { cModule = parent.module(LocatedString{Value: "C"}) } for _, node := range nodes { var name LocatedString var typ NodeType switch node.(type) { case *FunctionDeclNode: fdn := node.(*FunctionDeclNode) name, typ = fdn.Header.Name, NODE_FUNCTION if fdn.Header.IsMethod { continue // TODO is this right? } if fdn.Header.Attrs().Contains("c") || fdn.Attrs().Contains("c") { _, occupied := nameMap.modules["C"].types[name.Value] if occupied { startPos := name.Where.Start() log.Errorln("constructor", "[%s:%d:%d] Found duplicate definition of `%s`", tree.Source.Path, startPos.Line, startPos.Char, name.Value) log.Error("constructor", tree.Source.MarkSpan(name.Where)) prevPos := previousLocation[name.Value] log.Errorln("constructor", "[%s:%d:%d] Previous declaration was here", tree.Source.Path, prevPos.StartLine, prevPos.StartChar) log.Error("constructor", tree.Source.MarkSpan(prevPos)) shouldExit = true } cModule.types[name.Value] = typ previousLocation[name.Value] = name.Where continue } case *VarDeclNode: vd := node.(*VarDeclNode) name, typ = vd.Name, NODE_VARIABLE case *TypeDeclNode: vd := node.(*TypeDeclNode) name, typ = vd.Name, NODE_TYPE switch vd.Type.(type) { case *EnumTypeNode: typ = NODE_ENUM case *StructTypeNode: typ = NODE_STRUCT } case *UseDeclNode: udn := node.(*UseDeclNode) baseModuleName := udn.Module.Name if len(udn.Module.Modules) > 0 { baseModuleName = udn.Module.Modules[0] } mod, ok := modules[baseModuleName.Value] if !ok { startPos := baseModuleName.Where.Start() log.Errorln("constructor", "[%s:%d:%d] Unknown module `%s`", tree.Source.Path, startPos.Line, startPos.Char, baseModuleName.Value) log.Error("constructor", tree.Source.MarkSpan(baseModuleName.Where)) shouldExit = true } nameMap.modules[baseModuleName.Value] = MapNames(mod.Nodes, mod, modules, nil) modNameMap := nameMap.modules[baseModuleName.Value] for _, mod := range udn.Module.Modules { nextMap, ok := modNameMap.modules[mod.Value] if !ok { startPos := mod.Where.Start() log.Errorln("constructor", "[%s:%d:%d] Unknown module `%s`", tree.Source.Path, startPos.Line, startPos.Char, mod.Value) log.Error("constructor", tree.Source.MarkSpan(mod.Where)) shouldExit = true break } modNameMap = nextMap } name, typ = udn.Module.Name, NODE_MODULE default: continue } _, occupied := nameMap.types[name.Value] if occupied { startPos := name.Where.Start() log.Errorln("constructor", "[%s:%d:%d] Found duplicate definition of `%s`", tree.Source.Path, startPos.Line, startPos.Char, name.Value) log.Error("constructor", tree.Source.MarkSpan(name.Where)) prevPos := previousLocation[name.Value] log.Errorln("constructor", "[%s:%d:%d] Previous declaration was here", tree.Source.Path, prevPos.StartLine, prevPos.StartChar) log.Error("constructor", tree.Source.MarkSpan(prevPos)) shouldExit = true } nameMap.types[name.Value] = typ previousLocation[name.Value] = name.Where } if shouldExit { os.Exit(util.EXIT_FAILURE_CONSTRUCTOR) } return nameMap }
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 *Codegen) err(err string, stuff ...interface{}) { log.Error("codegen", util.TEXT_RED+util.TEXT_BOLD+"error:"+util.TEXT_RESET+" %s\n", fmt.Sprintf(err, stuff...)) os.Exit(util.EXIT_FAILURE_CODEGEN) }