using clojure-clr as script interpreter

548 views Asked by At

I have a very simple clojure interpreter set up in C# that loads a .clj file and makes the functions available for consumption within AutoCAD. This works well but I'd like to set this up with a bit more structure so that I can 'module-ize' the source files rather than having one great master file (which is the only way I can get it to work currently).

I've tried various methods such as import, use, require load and load-file within the scripts and also loading multiple files in the C# script code but I'd rather have one main script that ref's in the other files required when loaded into the interpreter.

Here is the C# snippet that I'm currently using to load the main file,

    Editor ed = _AcAp.Application.DocumentManager.MdiActiveDocument.Editor;
    clojure.lang.Compiler.loadFile(AppEntry.AppPath + "..\\Scripts\\main.clj");
    PromptResult res = ed.GetString("Enter a clojure command: ");
    // res should have the user entered command to invoke:
    var foo = clojure.lang.RT.var("main", res.StringResult);
    object o = foo.invoke();

Here is an example of 2 files I would like to have loaded at run time, the main file that would ref all others,

(ns main) ;; the main file that gets loaded into interpreter

(import 
    '(Teigha.DatabaseServices Line)
    '(Teigha.Geometry Point3d)
    '(dbtools add-to-db)) ;; my other 'script' file I would like imported for use

(defn add-line 
    []
    (let [ line (Line. (Point3d. 20.0 20.0 0.0) (Point3d. 200.0 50.0 0.0))]
         ;; call an external script file method
         (add-to-db line))) 

and the one that I would like to ref in, currently in the same folder as the main file but would like to organise these into sub folders at some stage.

(ns dbtools) ;; helper file/module

(import 
    '(Teigha.DatabaseServices Database SymbolUtilityServices 
                Transaction BlockTable BlockTableRecord OpenMode)
    '(Bricscad.ApplicationServices Application))

(defn add-to-db
    "Adds an AcDbEntity to ModelSpace of the current database
    Returns the ObjectId of the Entity added to the db."
    [entity]
    (let [ db (.. Application DocumentManager MdiActiveDocument Database)]
        (with-open [tr (.. db TransactionManager StartTransaction)]
            (let [  bt (.GetObject tr (.BlockTableId db) OpenMode/ForWrite)
                    btr(.GetObject tr (. SymbolUtilityServices GetBlockModelSpaceId db) OpenMode/ForWrite)]
                (let [id (.AppendEntity btr entity)]
                    (doto tr
                        (.AddNewlyCreatedDBObject entity true)
                        (.Commit))
                        id)))))

Any guidance on the best way to go about this? Thanks.

EDIT: I have it working with the following changes to the main file but I'm still open for better ways to do this, for instance - how to set the load path to match the main.clj file folder. Here's the changed file for reference:

(ns main) ;; the main file that gets loaded into interpreter

(load-file "C:\\path\\to\\dbtools.clj")
(require '[dbtools :as tools])

(import 
    '(Teigha.DatabaseServices Line)
    '(Teigha.Geometry Point3d))

(defn add-line []
    (let [ line (Line. (Point3d. 20.0 20.0 0.0) (Point3d. 200.0 50.0 0.0))]
         ;; call an external script file method
         (tools/add-to-db line)))
1

There are 1 answers

1
Matthew Molloy On

For Excel-REPL I define the following helper functions.

(import System.Environment)
(require '[clojure.string :as string])

(defn get-load-path []
  (set (string/split (Environment/GetEnvironmentVariable "CLOJURE_LOAD_PATH") #";")))

(defn set-load-path! [s]
  (let [
        new-path (apply str (interpose ";" s))
        ]
    (Environment/SetEnvironmentVariable "CLOJURE_LOAD_PATH" new-path)
    new-path))

Then I can simply update the load path

(set-load-path! (conj (get-load-path) "path/to/clojure/source/files"))

After that I can just require in the clj files and ClojureCLR knows where to find them.