Login
7 branches 0 tags
Ben (X13/Arch) Removed watcom support 6b7ba0e 3 years ago 850 Commits
nujel / stdlib / core / module.nuj
;; Nujel - Copyright (C) 2020-2021 - Benjamin Vincent Schulenburg
;; This project uses the MIT license, a copy should be included under /LICENSE
;;
;; Contains the module system
[def module/cache @[]]
[def module/store @[]]
[def module/loader #nil]

[defn module/add-loader [f]
      [set! module/loader [cons f module/loader]]]

[defn module/save-state []
      @[:cache module/cache :loader module/loader]]

[defn module/restore-state [c]
      [set! module/cache [tree/ref c :cache]]
      [set! module/loader [tree/ref c :loader]]]

[defn module/qualify-symbol [module-name symbol]
      [string->symbol [cat [keyword->symbol module-name] "/" [string symbol]]]]

[defmacro module body
          "Define a new module and return it"
          [macroexpand [cons 'environment* [cons '[def exports @[]] body]]]]

[defmacro defmodule [name . body]
          "Define a new named module"
          `[module/insert ~name [module [def *module* ~name] ~@body]]]

[defmacro defmodule/defer [name . body]
          "Define a new named module"
          `[module/insert/defer ~name [string/write ~[list 'quote [cons 'do body]]]]]

[defmacro export [name value]
          `[tree/set! exports '~name ~value]]

[defn require* [module env qualify?]
      [def mod [module/load module]]
      [def exports [resolve 'exports mod]]
      [doseq [k [tree/keys exports]]
             [def-in! env
               [if qualify?
                   [module/qualify-symbol module [keyword->symbol k]]
                   [keyword->symbol k]]
               [tree/ref exports k]]]]

[defmacro use [module]
          `[require* ~module [current-closure] #f]]

[defmacro require [module]
          `[require* ~module [current-closure] #t]]

[defmacro import* [local-symbol module module-symbol]
          `[def ~local-symbol [module/import ~module '~module-symbol]]]

[defmacro import [names module]
          [when-not [list? names]
                    [return `[import* ~names [module/load ~module] ~names]]]
          [def ret #nil]
          [while names
                 [if [= [cadr names] :as]
                     [do [set! ret [cons `[import* ~[caddr names] [module/load ~module] ~[car names]] ret]]
                         [set! names [cddr names]]]
                     [set! ret [cons `[import* ~[car names] [module/load ~module] ~[car names]] ret]]]
                 [cdr! names]]
          [cons do [nreverse ret]]]

[defn module/load/cache [name]
      [tree/ref module/cache name]]

[defn module/load/store [name]
      [def source [tree/ref module/store name]]
      [when source
        [eval-in [environment*] `[defmodule ~name ~@[read source]]]
        [module/load/cache name]]]

[defn module/load/external [name]
      [doseq [loader module/loader]
             [def mod [loader name]]
             [when mod
               [tree/set! module/cache name mod]
               [return mod]]]]

[defn module/insert/defer [name module-source]
      [tree/set! module/store name module-source]]

[defn module/insert [name module]
      [tree/set! module/cache name module]]

[defn module/load [name]
      [case [type-of name]
            [:object name]
            [:keyword [or [module/load/cache name]
                          [module/load/store name]
                          [module/load/external name]]]
            [otherwise [exception "Can't load that value as a module" name]]]]

[defn module/import-all [module symbol]
      [def exports [resolve 'exports module]]
      [typecheck/only exports :tree]
      [return exports]]

[defn module/import [module symbol]
      [def exports [module/import-all module symbol]]
      [when-not [tree/has? exports symbol]
                [exception :import-error [if [resolves? symbol module]
                                             "That symbol was not exported"
                                             "That symbol does not exist in that module"]]]
      [tree/ref exports symbol]]