|
3190 | 3190 |
|
3191 | 3191 | (defn eval
|
3192 | 3192 | "Evaluate a form (not a string) and return its result."
|
3193 |
| - [form] |
3194 |
| - (let [ctx (basilisp.lang.compiler.CompilerContext. "<Eval Input>")] |
3195 |
| - (basilisp.lang.compiler/compile-and-exec-form form |
3196 |
| - ctx |
3197 |
| - *ns*))) |
| 3193 | + ([form] |
| 3194 | + (eval form *ns*)) |
| 3195 | + ([form namespace] |
| 3196 | + (let [ctx (basilisp.lang.compiler.CompilerContext. "<Eval Input>")] |
| 3197 | + (basilisp.lang.compiler/compile-and-exec-form form |
| 3198 | + ctx |
| 3199 | + namespace)))) |
3198 | 3200 |
|
3199 | 3201 | ;;;;;;;;;;;;;;;;;;;
|
3200 | 3202 | ;; Ref Utilities ;;
|
|
3399 | 3401 | [module]
|
3400 | 3402 | `(import* ~module))
|
3401 | 3403 |
|
| 3404 | +(import importlib) |
| 3405 | + |
| 3406 | +(defn- require-libspec |
| 3407 | + "Convert a user-specified require libspec into a map with well-defined keys. |
| 3408 | + |
| 3409 | + Required keys: |
| 3410 | + - :namespace ns-sym, a symbol naming the Namespace being required |
| 3411 | + |
| 3412 | + Optional keys: |
| 3413 | + - :as ns-alias, a symbol which will alias the Namespace when required (if given) |
| 3414 | + - :refer [sym1, sym2], a sequence of symbols naming Vars to refer |
| 3415 | + - :refer :all, if every Var should be referred |
| 3416 | + - :only [sym1, sym2], a sequence of symbols naming Vars to refer |
| 3417 | + - :exclude [sym1, sym2], to refer all except the specified symbols |
| 3418 | + - :rename {sym1 new-sym1}, to rename all the specified symbols to the given new name" |
| 3419 | + [req] |
| 3420 | + (cond |
| 3421 | + (vector? req) (merge {:namespace (first req)} |
| 3422 | + (apply hash-map (rest req))) |
| 3423 | + (symbol? req) {:namespace req} |
| 3424 | + :else (throw |
| 3425 | + (ex-info "Invalid libspec for require" |
| 3426 | + {:value req})))) |
| 3427 | + |
| 3428 | +(defn- require-lib |
| 3429 | + "Require the library described by `libspec` into the Namespace `requiring-ns`." |
| 3430 | + [requiring-ns libspec] |
| 3431 | + (let [required-ns-sym (:namespace libspec)] |
| 3432 | + ;; In order to enable direct linking of Vars as Python variables, required |
| 3433 | + ;; namespaces must be `require*`ed into the namespace. That's not possible |
| 3434 | + ;; to do without a macro, so we're using this hacky approach to eval the |
| 3435 | + ;; code directly (which will effectively add it to the root namespace module). |
| 3436 | + (eval (list 'require* |
| 3437 | + (if-let [ns-alias (:as libspec)] |
| 3438 | + [required-ns-sym :as ns-alias] |
| 3439 | + required-ns-sym)) |
| 3440 | + requiring-ns) |
| 3441 | + ;; Reset the namespace to the requiring namespace, since it was likely changed |
| 3442 | + ;; during the require process |
| 3443 | + (set! *ns* requiring-ns))) |
| 3444 | + |
| 3445 | +(defn- refer-filtered-interns |
| 3446 | + "Return a map of symbols to interned Vars in the Namespace `referred-ns` subject |
| 3447 | + to the filters described in `libspec`." |
| 3448 | + [referred-ns libspec] |
| 3449 | + (when (= :all (:refer libspec)) |
| 3450 | + (throw |
| 3451 | + (ex-info "cannot specify :refer :all for refer; use (refer 'ns-sym) instead" |
| 3452 | + {:libspec libspec}))) |
| 3453 | + (let [only (set (or (:refer libspec) (:only libspec))) |
| 3454 | + exclude (set (:exclude libspec)) |
| 3455 | + rename (get libspec :rename {})] |
| 3456 | + (cond->> (ns-interns referred-ns) |
| 3457 | + (seq only) (filter (fn [entry] |
| 3458 | + (contains? only (key entry)))) |
| 3459 | + (seq exclude) (remove (fn [entry] |
| 3460 | + (contains? exclude (key entry)))) |
| 3461 | + (seq rename) (reduce (fn [m entry] |
| 3462 | + (if (rename (key entry)) |
| 3463 | + (assoc m (rename (key entry)) (val entry)) |
| 3464 | + m)) |
| 3465 | + {})))) |
| 3466 | + |
| 3467 | +(defn- refer-lib |
| 3468 | + "Refer Vars into `requiring-ns` as described by `libspec`. |
| 3469 | + |
| 3470 | + This function assumes the referred-to Namespace has already been loaded by |
| 3471 | + `require-lib`." |
| 3472 | + [requiring-ns libspec] |
| 3473 | + (let [referred-ns (the-ns (:namespace libspec))] |
| 3474 | + (if-not (some #(contains? libspec %) [:refer :only :exclude :rename]) |
| 3475 | + (.refer-all requiring-ns referred-ns) |
| 3476 | + (doseq [intern-entry (refer-filtered-interns referred-ns libspec)] |
| 3477 | + (let [sym (key intern-entry) |
| 3478 | + var (val intern-entry)] |
| 3479 | + (.add-refer requiring-ns sym var)))))) |
| 3480 | + |
| 3481 | +(defn require |
| 3482 | + "Load Basilisp libraries and make them accessible in the current namespace. |
| 3483 | + |
| 3484 | + Arguments should be libspecs, which take the following forms: |
| 3485 | + - symbols, which name fully qualified namespaces |
| 3486 | + - vectors, which take the form [namespace-symbol & opts] |
| 3487 | + |
| 3488 | + Vector libspec arguments must be one of: |
| 3489 | + - :as name, which will alias the imported namespace to the symbol name |
| 3490 | + - :refer [& syms], which will refer syms in the local namespace directly |
| 3491 | + - :refer :all, which will refer all symbols from the namespace directly |
| 3492 | + |
| 3493 | + Use of `require` directly is discouraged in favor of the `:require` directive in |
| 3494 | + the `ns` macro." |
| 3495 | + [& args] |
| 3496 | + (let [current-ns *ns*] |
| 3497 | + (doseq [libspec (map require-libspec args)] |
| 3498 | + (require-lib current-ns libspec) |
| 3499 | + |
| 3500 | + ;; Add refers |
| 3501 | + (let [new-ns (the-ns (:namespace libspec)) |
| 3502 | + refer-opt (:refer libspec)] |
| 3503 | + (cond |
| 3504 | + (= :all refer-opt) |
| 3505 | + (.refer-all current-ns new-ns) |
| 3506 | + |
| 3507 | + (seq refer-opt) |
| 3508 | + (let [new-ns-interns (ns-interns new-ns)] |
| 3509 | + (doseq [var-sym refer-opt] |
| 3510 | + (let [var (get new-ns-interns var-sym)] |
| 3511 | + (.add-refer current-ns var-sym var)))) |
| 3512 | + |
| 3513 | + :else nil))) |
| 3514 | + nil)) |
| 3515 | + |
3402 | 3516 | (defn refer
|
3403 | 3517 | "Refer Vars from the namespace named by ns-sym, subject to the filters specified.
|
3404 | 3518 |
|
3405 | 3519 | Supported filters:
|
3406 | 3520 | - :only [sym1 sym2], to only refer the specified symbols
|
3407 | 3521 | - :exclude [sym1, sym2], to refer all except the specified symbols
|
3408 |
| - - :rename {sym1 new-sym1}, to rename all the specified symbols to the given new name" |
| 3522 | + - :rename {sym1 new-sym1}, to rename all the specified symbols to the given new name |
| 3523 | + |
| 3524 | + Use of `refer` directly is discouraged in favor of the `:refer` modifier in the |
| 3525 | + `:require` directive of the `ns` macro or the `:use` directive of the `ns` macro." |
3409 | 3526 | [ns-sym & filters]
|
3410 |
| - (let [current-ns *ns* |
3411 |
| - ns (the-ns ns-sym) |
3412 |
| - filter-map (apply hash-map filters)] |
3413 |
| - (if-not (seq filters) |
3414 |
| - (.refer-all current-ns ns) |
3415 |
| - (let [only (set (:only filter-map)) |
3416 |
| - exclude (set (:exclude filter-map)) |
3417 |
| - rename (or (:rename filter-map) {}) |
3418 |
| - |
3419 |
| - interns (if (seq only) |
3420 |
| - (filter (fn [entry] |
3421 |
| - (contains? only (key entry))) |
3422 |
| - (ns-interns ns)) |
3423 |
| - (ns-interns ns)) |
3424 |
| - interns (if (seq exclude) |
3425 |
| - (remove (fn [entry] |
3426 |
| - (contains? exclude (key entry))) |
3427 |
| - interns) |
3428 |
| - interns) |
3429 |
| - interns (if (seq rename) |
3430 |
| - (reduce (fn [m entry] |
3431 |
| - (if (rename (key entry)) |
3432 |
| - (assoc m (rename (key entry)) (val entry)) |
3433 |
| - m)) |
3434 |
| - {} |
3435 |
| - interns) |
3436 |
| - interns) |
3437 |
| - |
3438 |
| - do-refer (fn [refers] |
3439 |
| - (when (seq refers) |
3440 |
| - (let [entry (first (seq refers)) |
3441 |
| - sym (key entry) |
3442 |
| - var (val entry)] |
3443 |
| - (.add-refer current-ns sym var) |
3444 |
| - (recur (rest refers)))))] |
3445 |
| - (do-refer interns))))) |
| 3527 | + (let [current-ns *ns*] |
| 3528 | + ;; It is necessary to first require the Namespace directly, otherwise |
| 3529 | + ;; when refer attempts to load the Namespace later, it will fail. |
| 3530 | + (->> (require-libspec ns-sym) |
| 3531 | + (require-lib current-ns)) |
| 3532 | + (->> (apply vector ns-sym filters) |
| 3533 | + (require-libspec) |
| 3534 | + (refer-lib current-ns)))) |
3446 | 3535 |
|
3447 | 3536 | (def refer-basilisp
|
3448 | 3537 | "Refer Vars from basilisp.core using the same filter syntax as refer."
|
|
3452 | 3541 | "Compatibility layer with JVM Clojure, which points to refer-basilisp."
|
3453 | 3542 | refer-basilisp)
|
3454 | 3543 |
|
3455 |
| -(import importlib) |
3456 |
| - |
3457 |
| -(defn ^:private add-refers |
3458 |
| - "Refer all of the Vars named in syms from from-ns to to-ns." |
3459 |
| - [to-ns from-ns syms] |
3460 |
| - (when (seq syms) |
3461 |
| - (let [var-sym (first syms) |
3462 |
| - var (get (ns-interns from-ns) var-sym)] |
3463 |
| - (.add-refer to-ns var-sym var) |
3464 |
| - (recur to-ns from-ns (rest syms))))) |
3465 |
| - |
3466 |
| -(defn ^:private require-vec |
3467 |
| - "Process a vector libspec for require." |
3468 |
| - [current-ns v] |
3469 |
| - (let [ns-sym (first v) |
3470 |
| - opts (apply hash-map (rest v))] |
3471 |
| - (importlib/import-module (name ns-sym)) |
3472 |
| - (let [new-ns (the-ns ns-sym)] |
3473 |
| - ;; Add the namespace alias |
3474 |
| - (.add-alias current-ns new-ns (or (:as opts) ns-sym)) |
3475 |
| - |
3476 |
| - ;; Add refers |
3477 |
| - (cond |
3478 |
| - (= :all (:refer opts)) |
3479 |
| - (.refer-all current-ns new-ns) |
3480 |
| - |
3481 |
| - (:refer opts) |
3482 |
| - (add-refers current-ns new-ns (:refer opts)) |
3483 |
| - |
3484 |
| - :else nil)))) |
3485 |
| - |
3486 |
| -(defn ^:private require-sym |
3487 |
| - "Process a symbol libspec for require." |
3488 |
| - [s] |
3489 |
| - (importlib/import-module (name s))) |
3490 |
| - |
3491 |
| -(defn require |
| 3544 | +(defn use |
3492 | 3545 | "Load Basilisp libraries and make them accessible in the current namespace.
|
3493 | 3546 |
|
3494 | 3547 | Arguments should be libspecs, which take the following forms:
|
3495 | 3548 | - symbols, which name fully qualified namespaces
|
3496 | 3549 | - vectors, which take the form [namespace-symbol & opts]
|
3497 | 3550 |
|
3498 |
| - Vector libspec arguments must be one of: |
3499 |
| - - :as name, which will alias the imported namespace to the symbol name |
3500 |
| - - :refer [& syms], which will refer syms in the local namespace directly" |
| 3551 | + `use` is like `require` which also refers all Vars from the requiring Namespace |
| 3552 | + afterwards. Libspecs passed to `use` may include filters as defined in `refer` to |
| 3553 | + narrow down the referred Vars. |
| 3554 | + |
| 3555 | + Supported filters: |
| 3556 | + - :only [sym1 sym2], to only refer the specified symbols |
| 3557 | + - :exclude [sym1, sym2], to refer all except the specified symbols |
| 3558 | + - :rename {sym1 new-sym1}, to rename all the specified symbols to the given new name |
| 3559 | + |
| 3560 | + Use of `use` directly is discouraged in favor of the `:use` directive in the `ns` |
| 3561 | + macro." |
3501 | 3562 | [& args]
|
3502 |
| - (let [current-ns *ns* |
3503 |
| - |
3504 |
| - do-require (fn [requires] |
3505 |
| - (when (seq requires) |
3506 |
| - (let [v (first requires)] |
3507 |
| - (cond |
3508 |
| - (vector? v) (require-vec current-ns v) |
3509 |
| - (symbol? v) (require-sym v) |
3510 |
| - :else (throw |
3511 |
| - (ex-info "Invalid libspec for require" |
3512 |
| - {:value v})))) |
3513 |
| - (recur (rest requires))))] |
3514 |
| - (do-require args) |
3515 |
| - (set! *ns* current-ns) |
| 3563 | + (let [current-ns *ns*] |
| 3564 | + (doseq [libspec (map require-libspec args)] |
| 3565 | + ;; Remove the :refer key to avoid having require-lib |
| 3566 | + ;; perform a full :refer, instead we'll use refer-lib |
| 3567 | + (->> (dissoc libspec :refer) |
| 3568 | + (require-lib current-ns)) |
| 3569 | + (refer-lib current-ns libspec)) |
3516 | 3570 | nil))
|
3517 | 3571 |
|
3518 | 3572 | (defmacro ns
|
|
3529 | 3583 | (:refer-basilisp :exclude [get])
|
3530 | 3584 | (:require
|
3531 | 3585 | [basilisp.string :as str])
|
| 3586 | + (:use |
| 3587 | + [basilisp.set :only [intersection]]) |
3532 | 3588 | (:import inspect))"
|
3533 | 3589 | [name & opts]
|
3534 | 3590 | (when-not (and (symbol? name) (nil? (namespace name)))
|
|
3548 | 3604 | {}
|
3549 | 3605 | opts))
|
3550 | 3606 |
|
3551 |
| - requires (when (:require opts) |
3552 |
| - `(require ~@(map #(list 'quote %) (:require opts)))) |
3553 |
| - imports (when (:import opts) |
3554 |
| - (map (fn [v] |
3555 |
| - `(import ~v)) |
3556 |
| - (:import opts)))] |
| 3607 | + refer-filters (when-let [filters (or (:refer-basilisp opts) |
| 3608 | + (:refer-clojure opts))] |
| 3609 | + (map #(list 'quote %) filters)) |
| 3610 | + requires (when (:require opts) |
| 3611 | + `(require ~@(map #(list 'quote %) (:require opts)))) |
| 3612 | + uses (when (:use opts) |
| 3613 | + `(use ~@(map #(list 'quote %) (:use opts)))) |
| 3614 | + imports (when (:import opts) |
| 3615 | + (map (fn [v] |
| 3616 | + `(import ~v)) |
| 3617 | + (:import opts)))] |
3557 | 3618 | `(do
|
3558 | 3619 | (in-ns (quote ~name))
|
3559 |
| - (refer-basilisp ~@(or (:refer-basilisp opts) |
3560 |
| - (:refer-clojure opts))) |
| 3620 | + (refer-basilisp ~@refer-filters) |
3561 | 3621 | ~requires
|
| 3622 | + ~uses |
3562 | 3623 | ~@imports)))
|
3563 | 3624 |
|
3564 | 3625 | ;;;;;;;;;;;;;;;;;;;;;
|
|
0 commit comments