loader.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. package loader
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. . "kumachan/error"
  6. "kumachan/parser"
  7. "kumachan/parser/ast"
  8. "kumachan/transformer"
  9. "kumachan/transformer/node"
  10. "os"
  11. "path/filepath"
  12. )
  13. type Module struct {
  14. Name string
  15. Node node.Module
  16. AST *ast.Tree
  17. ImpMap map[string] *Module
  18. FileInfo os.FileInfo
  19. }
  20. type Index map[string] *Module
  21. func ReadFile(path string) ([]byte, os.FileInfo, error) {
  22. var fd, err1 = os.Open(path)
  23. if err1 != nil { return nil, nil, err1 }
  24. defer fd.Close()
  25. var info, err2 = fd.Stat()
  26. if err2 != nil { return nil, nil, err2 }
  27. var content, err3 = ioutil.ReadAll(fd)
  28. if err3 != nil { return nil, nil, err3 }
  29. return content, info, nil
  30. }
  31. func LoadModule(path string, ctx Context, idx Index) (*Module, *Error) {
  32. /* 1. Try to read the content of given source file */
  33. var file_content, file_info, err1 = ReadFile(path)
  34. if err1 != nil { return nil, &Error {
  35. Context: ctx,
  36. Concrete: E_ReadFileFailed {
  37. FilePath: path,
  38. Message: err1.Error(),
  39. },
  40. } }
  41. /* 2. Try to parse the content and generate an AST */
  42. var code_string = string(file_content)
  43. var code = []rune(code_string)
  44. var tree, err2 = parser.Parse(code, "module", path)
  45. if err2 != nil { return nil, &Error {
  46. Context: ctx,
  47. Concrete: E_ParseFailed {
  48. ParserError: err2,
  49. },
  50. } }
  51. /* 3. Transform the AST to typed structures */
  52. var module_node = transformer.Transform(tree)
  53. /* 4. Extract the module name */
  54. var module_name = string(module_node.Name.Name)
  55. /* 5. Check the module name according to ancestor modules */
  56. for _, ancestor := range ctx.BreadCrumbs {
  57. if ancestor.ModuleName == module_name {
  58. /* 5.1. If there is an ancestor with the same name: */
  59. if os.SameFile(ancestor.FileInfo, file_info) {
  60. /* 5.1.1. If it corresponds to the same source file, */
  61. /* throw an error of circular import. */
  62. return nil, &Error {
  63. Context: ctx,
  64. Concrete: E_CircularImport {
  65. ModuleName: module_name,
  66. },
  67. }
  68. } else {
  69. /* 5.1.2. Otherwise, throw an error of module name conflict. */
  70. return nil, &Error {
  71. Context: ctx,
  72. Concrete: E_NameConflict {
  73. ModuleName: module_name,
  74. FilePath1: ancestor.FilePath,
  75. FilePath2: path,
  76. },
  77. }
  78. }
  79. }
  80. }
  81. /* 6. Check the module name according to previous sibling (sub)modules */
  82. var existing, exists = idx[module_name]
  83. if exists {
  84. /* 6.1. If there is a sibling (sub)module with the same name */
  85. if os.SameFile(existing.FileInfo, file_info) {
  86. /* 6.1.1. If it corresponds to the same source file, */
  87. /* which indicates the module has already been loaded, */
  88. /* return the loaded module. */
  89. return existing, nil
  90. } else {
  91. /* 6.1.2. Otherwise, throw an error of module name conflict. */
  92. return nil, &Error {
  93. Context: ctx,
  94. Concrete: E_NameConflict {
  95. ModuleName: module_name,
  96. FilePath1: existing.AST.Name,
  97. FilePath2: path,
  98. },
  99. }
  100. }
  101. } else {
  102. /* 6.2. Otherwise, load all submodules of current module */
  103. /* and then return the current module. */
  104. var imported_map = make(map[string] *Module)
  105. var imported_set = make(map[string] bool)
  106. ImportStdLib(imported_map, imported_set)
  107. var current_breadcrumbs = append(ctx.BreadCrumbs, Ancestor {
  108. ModuleName: module_name,
  109. FileInfo: file_info,
  110. FilePath: path,
  111. })
  112. for _, cmd := range module_node.Commands {
  113. switch c := cmd.Command.(type) {
  114. case node.Import:
  115. // Execute each `import` command
  116. var local_alias = string(c.Name.Name)
  117. var relpath = string(c.Path.Value)
  118. var imctx = Context {
  119. ImportPoint: ErrorPoint {
  120. AST: tree,
  121. Node: c.Node,
  122. },
  123. LocalAlias: local_alias,
  124. BreadCrumbs: current_breadcrumbs,
  125. }
  126. var _, exists = imported_map[local_alias]
  127. if exists {
  128. return nil, &Error {
  129. Context: imctx,
  130. Concrete: E_ConflictAlias {
  131. LocalAlias: local_alias,
  132. },
  133. }
  134. }
  135. var impath = filepath.Join(filepath.Dir(path), relpath)
  136. var immod, err = LoadModule(impath, imctx, idx)
  137. if err != nil {
  138. // Bubble errors
  139. return nil, err
  140. } else {
  141. // Register the imported module
  142. var immod_name = string(immod.Node.Name.Name)
  143. if imported_set[immod_name] { return nil, &Error {
  144. Context: imctx,
  145. Concrete: E_DuplicateImport {
  146. ModuleName: immod_name,
  147. },
  148. } }
  149. imported_set[immod_name] = true
  150. imported_map[local_alias] = immod
  151. idx[immod_name] = immod
  152. }
  153. default:
  154. // do nothing
  155. }
  156. }
  157. var mod = &Module {
  158. Name: module_name,
  159. Node: module_node,
  160. ImpMap: imported_map,
  161. AST: tree,
  162. FileInfo: file_info,
  163. }
  164. idx[module_name] = mod
  165. return mod, nil
  166. }
  167. }
  168. func LoadEntry (path string) (*Module, Index, *Error) {
  169. var idx = __StdLibIndex
  170. var ctx = MakeEntryContext()
  171. var mod, err = LoadModule(path, ctx, idx)
  172. return mod, idx, err
  173. }
  174. var __StdLibModules = []string { "core", "io", "os" }
  175. var __StdLibIndex = make(map[string] *Module)
  176. var _ = __Init()
  177. func __Init() interface{} {
  178. LoadStdLib()
  179. return nil
  180. }
  181. func LoadStdLib() {
  182. var exe_path, err = os.Executable()
  183. if err != nil { panic(err) }
  184. var ctx = MakeEntryContext()
  185. for _, name := range __StdLibModules {
  186. var file = filepath.Join(filepath.Dir(exe_path), "stdlib", name + ".km")
  187. var _, err = LoadModule(file, ctx, __StdLibIndex)
  188. if err != nil {
  189. fmt.Fprintf (
  190. os.Stderr,
  191. "%v*** Failed to Load Standard Library%v\n*\n%s\n",
  192. "\033[1m", "\033[0m", err.Error(),
  193. )
  194. os.Exit(3)
  195. }
  196. }
  197. }
  198. func ImportStdLib (imp_map map[string]*Module, imp_set map[string]bool) {
  199. for name, mod := range __StdLibIndex {
  200. imp_map[name] = mod
  201. imp_set[name] = true
  202. }
  203. }