123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- package loader
- import (
- "fmt"
- "io/ioutil"
- . "kumachan/error"
- "kumachan/parser"
- "kumachan/parser/ast"
- "kumachan/transformer"
- "kumachan/transformer/node"
- "os"
- "path/filepath"
- )
- type Module struct {
- Name string
- Node node.Module
- AST *ast.Tree
- ImpMap map[string] *Module
- FileInfo os.FileInfo
- }
- type Index map[string] *Module
- func ReadFile(path string) ([]byte, os.FileInfo, error) {
- var fd, err1 = os.Open(path)
- if err1 != nil { return nil, nil, err1 }
- defer fd.Close()
- var info, err2 = fd.Stat()
- if err2 != nil { return nil, nil, err2 }
- var content, err3 = ioutil.ReadAll(fd)
- if err3 != nil { return nil, nil, err3 }
- return content, info, nil
- }
- func LoadModule(path string, ctx Context, idx Index) (*Module, *Error) {
- /* 1. Try to read the content of given source file */
- var file_content, file_info, err1 = ReadFile(path)
- if err1 != nil { return nil, &Error {
- Context: ctx,
- Concrete: E_ReadFileFailed {
- FilePath: path,
- Message: err1.Error(),
- },
- } }
- /* 2. Try to parse the content and generate an AST */
- var code_string = string(file_content)
- var code = []rune(code_string)
- var tree, err2 = parser.Parse(code, "module", path)
- if err2 != nil { return nil, &Error {
- Context: ctx,
- Concrete: E_ParseFailed {
- ParserError: err2,
- },
- } }
- /* 3. Transform the AST to typed structures */
- var module_node = transformer.Transform(tree)
- /* 4. Extract the module name */
- var module_name = string(module_node.Name.Name)
- /* 5. Check the module name according to ancestor modules */
- for _, ancestor := range ctx.BreadCrumbs {
- if ancestor.ModuleName == module_name {
- /* 5.1. If there is an ancestor with the same name: */
- if os.SameFile(ancestor.FileInfo, file_info) {
- /* 5.1.1. If it corresponds to the same source file, */
- /* throw an error of circular import. */
- return nil, &Error {
- Context: ctx,
- Concrete: E_CircularImport {
- ModuleName: module_name,
- },
- }
- } else {
- /* 5.1.2. Otherwise, throw an error of module name conflict. */
- return nil, &Error {
- Context: ctx,
- Concrete: E_NameConflict {
- ModuleName: module_name,
- FilePath1: ancestor.FilePath,
- FilePath2: path,
- },
- }
- }
- }
- }
- /* 6. Check the module name according to previous sibling (sub)modules */
- var existing, exists = idx[module_name]
- if exists {
- /* 6.1. If there is a sibling (sub)module with the same name */
- if os.SameFile(existing.FileInfo, file_info) {
- /* 6.1.1. If it corresponds to the same source file, */
- /* which indicates the module has already been loaded, */
- /* return the loaded module. */
- return existing, nil
- } else {
- /* 6.1.2. Otherwise, throw an error of module name conflict. */
- return nil, &Error {
- Context: ctx,
- Concrete: E_NameConflict {
- ModuleName: module_name,
- FilePath1: existing.AST.Name,
- FilePath2: path,
- },
- }
- }
- } else {
- /* 6.2. Otherwise, load all submodules of current module */
- /* and then return the current module. */
- var imported_map = make(map[string] *Module)
- var imported_set = make(map[string] bool)
- ImportStdLib(imported_map, imported_set)
- var current_breadcrumbs = append(ctx.BreadCrumbs, Ancestor {
- ModuleName: module_name,
- FileInfo: file_info,
- FilePath: path,
- })
- for _, cmd := range module_node.Commands {
- switch c := cmd.Command.(type) {
- case node.Import:
- // Execute each `import` command
- var local_alias = string(c.Name.Name)
- var relpath = string(c.Path.Value)
- var imctx = Context {
- ImportPoint: ErrorPoint {
- AST: tree,
- Node: c.Node,
- },
- LocalAlias: local_alias,
- BreadCrumbs: current_breadcrumbs,
- }
- var _, exists = imported_map[local_alias]
- if exists {
- return nil, &Error {
- Context: imctx,
- Concrete: E_ConflictAlias {
- LocalAlias: local_alias,
- },
- }
- }
- var impath = filepath.Join(filepath.Dir(path), relpath)
- var immod, err = LoadModule(impath, imctx, idx)
- if err != nil {
- // Bubble errors
- return nil, err
- } else {
- // Register the imported module
- var immod_name = string(immod.Node.Name.Name)
- if imported_set[immod_name] { return nil, &Error {
- Context: imctx,
- Concrete: E_DuplicateImport {
- ModuleName: immod_name,
- },
- } }
- imported_set[immod_name] = true
- imported_map[local_alias] = immod
- idx[immod_name] = immod
- }
- default:
- // do nothing
- }
- }
- var mod = &Module {
- Name: module_name,
- Node: module_node,
- ImpMap: imported_map,
- AST: tree,
- FileInfo: file_info,
- }
- idx[module_name] = mod
- return mod, nil
- }
- }
- func LoadEntry (path string) (*Module, Index, *Error) {
- var idx = __StdLibIndex
- var ctx = MakeEntryContext()
- var mod, err = LoadModule(path, ctx, idx)
- return mod, idx, err
- }
- var __StdLibModules = []string { "core", "io", "os" }
- var __StdLibIndex = make(map[string] *Module)
- var _ = __Init()
- func __Init() interface{} {
- LoadStdLib()
- return nil
- }
- func LoadStdLib() {
- var exe_path, err = os.Executable()
- if err != nil { panic(err) }
- var ctx = MakeEntryContext()
- for _, name := range __StdLibModules {
- var file = filepath.Join(filepath.Dir(exe_path), "stdlib", name + ".km")
- var _, err = LoadModule(file, ctx, __StdLibIndex)
- if err != nil {
- fmt.Fprintf (
- os.Stderr,
- "%v*** Failed to Load Standard Library%v\n*\n%s\n",
- "\033[1m", "\033[0m", err.Error(),
- )
- os.Exit(3)
- }
- }
- }
- func ImportStdLib (imp_map map[string]*Module, imp_set map[string]bool) {
- for name, mod := range __StdLibIndex {
- imp_map[name] = mod
- imp_set[name] = true
- }
- }
|