application/octet-stream
•
3.92 KB
•
109 lines
;; 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 q [if qualify?
[module/qualify-symbol module [keyword->symbol k]]
[keyword->symbol k]]]
[def expr `[def ~q [module/import ~mod '~[keyword->symbol k]]]]
[bytecode-eval* [compile* expr env] env]]]
[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]]