diff options
author | Eduardo Julian | 2017-01-29 19:28:36 -0400 |
---|---|---|
committer | Eduardo Julian | 2017-01-29 19:28:36 -0400 |
commit | e4f2969ff13ad2b7a16299d8627e9188de555390 (patch) | |
tree | dd738fd413231979b9e97377df780d21cf39a51b /luxc/src | |
parent | 7886f9da86c2b6d3da6ab801d07005d21686c275 (diff) |
- Major refactoring to make it easier to introduce the new (JS) backend.
Diffstat (limited to '')
-rw-r--r-- | luxc/src/lux.clj | 1 | ||||
-rw-r--r-- | luxc/src/lux/analyser.clj | 4 | ||||
-rw-r--r-- | luxc/src/lux/analyser/jvm.clj (renamed from luxc/src/lux/analyser/host.clj) | 4 | ||||
-rw-r--r-- | luxc/src/lux/analyser/module.clj | 13 | ||||
-rw-r--r-- | luxc/src/lux/compiler.clj | 276 | ||||
-rw-r--r-- | luxc/src/lux/compiler/core.clj | 82 | ||||
-rw-r--r-- | luxc/src/lux/compiler/io.clj | 2 | ||||
-rw-r--r-- | luxc/src/lux/compiler/jvm.clj | 228 | ||||
-rw-r--r-- | luxc/src/lux/compiler/jvm/base.clj (renamed from luxc/src/lux/compiler/base.clj) | 37 | ||||
-rw-r--r-- | luxc/src/lux/compiler/jvm/cache.clj (renamed from luxc/src/lux/compiler/cache.clj) | 35 | ||||
-rw-r--r-- | luxc/src/lux/compiler/jvm/case.clj (renamed from luxc/src/lux/compiler/case.clj) | 4 | ||||
-rw-r--r-- | luxc/src/lux/compiler/jvm/host.clj (renamed from luxc/src/lux/compiler/host.clj) | 4 | ||||
-rw-r--r-- | luxc/src/lux/compiler/jvm/lambda.clj (renamed from luxc/src/lux/compiler/lambda.clj) | 4 | ||||
-rw-r--r-- | luxc/src/lux/compiler/jvm/lux.clj (renamed from luxc/src/lux/compiler/lux.clj) | 6 | ||||
-rw-r--r-- | luxc/src/lux/compiler/module.clj | 23 | ||||
-rw-r--r-- | luxc/src/lux/repl.clj | 2 |
16 files changed, 384 insertions, 341 deletions
diff --git a/luxc/src/lux.clj b/luxc/src/lux.clj index 76778346d..182ddf46f 100644 --- a/luxc/src/lux.clj +++ b/luxc/src/lux.clj @@ -1,7 +1,6 @@ (ns lux (:gen-class) (:require [lux.base :as & :refer [|let |do return return* |case]] - [lux.compiler.base :as &compiler-base] [lux.compiler :as &compiler] [lux.repl :as &repl] [clojure.string :as string] diff --git a/luxc/src/lux/analyser.clj b/luxc/src/lux/analyser.clj index 51b5b4028..614bc0a34 100644 --- a/luxc/src/lux/analyser.clj +++ b/luxc/src/lux/analyser.clj @@ -9,7 +9,7 @@ [host :as &host]) (lux.analyser [base :as &&] [lux :as &&lux] - [host :as &&host] + [jvm :as &&jvm] [module :as &&module] [parser :as &&a-parser]))) @@ -130,7 +130,7 @@ (&/$Cons [_ (&/$TupleS ?args)] (&/$Nil))) parameters] (&/with-analysis-meta cursor exo-type - (&&host/analyse-host analyse exo-type compilers ?category ?proc ?args))) + (&&jvm/analyse-host analyse exo-type compilers ?category ?proc ?args))) "_lux_:" (|let [(&/$Cons ?type diff --git a/luxc/src/lux/analyser/host.clj b/luxc/src/lux/analyser/jvm.clj index d89de457b..24d2b2017 100644 --- a/luxc/src/lux/analyser/host.clj +++ b/luxc/src/lux/analyser/jvm.clj @@ -1,4 +1,4 @@ -(ns lux.analyser.host +(ns lux.analyser.jvm (:require (clojure [template :refer [do-template]] [string :as string]) clojure.core.match @@ -15,7 +15,7 @@ [lambda :as &&lambda] [env :as &&env] [parser :as &&a-parser]) - [lux.compiler.base :as &c!base]) + [lux.compiler.jvm.base :as &c!base]) (:import (java.lang.reflect Type TypeVariable))) ;; [Utils] diff --git a/luxc/src/lux/analyser/module.clj b/luxc/src/lux/analyser/module.clj index c6a079cab..3ccb887ff 100644 --- a/luxc/src/lux/analyser/module.clj +++ b/luxc/src/lux/analyser/module.clj @@ -396,3 +396,16 @@ _ (&/fail-with-loc "[Analyser Error] No import meta-data."))) + +(def tag-groups + "(Lux (List [Text (List Text)]))" + (|do [module &/get-current-module] + (return (&/|map (fn [pair] + (|case pair + [name [tags exported? _]] + (&/T [name (&/|map (fn [tag] + (|let [[t-prefix t-name] tag] + t-name)) + tags)]))) + (&/get$ $types module))) + )) diff --git a/luxc/src/lux/compiler.clj b/luxc/src/lux/compiler.clj index 4792a1809..fafb35818 100644 --- a/luxc/src/lux/compiler.clj +++ b/luxc/src/lux/compiler.clj @@ -1,267 +1,35 @@ (ns lux.compiler (:refer-clojure :exclude [compile]) - (:require (clojure [string :as string] - [set :as set] - [template :refer [do-template]]) - clojure.core.match + (:require clojure.core.match clojure.core.match.array - (lux [base :as & :refer [|let |do return* return |case]] - [type :as &type] - [reader :as &reader] - [lexer :as &lexer] - [parser :as &parser] - [analyser :as &analyser] - [optimizer :as &optimizer] - [host :as &host]) - [lux.host.generics :as &host-generics] - [lux.optimizer :as &o] - [lux.analyser.base :as &a] - [lux.analyser.module :as &a-module] - (lux.compiler [base :as &&] - [cache :as &&cache] - [lux :as &&lux] - [host :as &&host] - [case :as &&case] - [lambda :as &&lambda] - [module :as &&module] + (lux [base :as & :refer [|let |do return* return |case]]) + (lux.compiler [core :as &&core] [io :as &&io] - [parallel :as &¶llel]) - (lux.compiler.cache [type :as &&&type] - [ann :as &&&ann])) - (:import (org.objectweb.asm Opcodes - Label - ClassWriter - MethodVisitor))) + [parallel :as &¶llel] + [jvm :as &&jvm] + ;; [js :as &&js] + ))) -;; [Resources] -(def ^:private !source->last-line (atom nil)) - -(defn compile-expression [$begin syntax] - (|let [[[?type [_file-name _line _]] ?form] syntax] - (|do [^MethodVisitor *writer* &/get-writer - :let [debug-label (new Label) - _ (when (not= _line (get @!source->last-line _file-name)) - (doto *writer* - (.visitLabel debug-label) - (.visitLineNumber (int _line) debug-label)) - (swap! !source->last-line assoc _file-name _line))]] - (|case ?form - (&o/$bool ?value) - (&&lux/compile-bool ?value) - - (&o/$nat ?value) - (&&lux/compile-nat ?value) - - (&o/$int ?value) - (&&lux/compile-int ?value) - - (&o/$deg ?value) - (&&lux/compile-deg ?value) - - (&o/$real ?value) - (&&lux/compile-real ?value) - - (&o/$char ?value) - (&&lux/compile-char ?value) - - (&o/$text ?value) - (&&lux/compile-text ?value) - - (&o/$tuple ?elems) - (&&lux/compile-tuple (partial compile-expression $begin) ?elems) - - (&o/$var (&/$Local ?idx)) - (&&lux/compile-local (partial compile-expression $begin) ?idx) - - (&o/$captured ?scope ?captured-id ?source) - (&&lux/compile-captured (partial compile-expression $begin) ?scope ?captured-id ?source) - - (&o/$var (&/$Global ?owner-class ?name)) - (&&lux/compile-global (partial compile-expression $begin) ?owner-class ?name) - - (&o/$apply ?fn ?args) - (&&lux/compile-apply (partial compile-expression $begin) ?fn ?args) - - (&o/$loop _register-offset _inits _body) - (&&lux/compile-loop compile-expression _register-offset _inits _body) - - (&o/$iter _register-offset ?args) - (&&lux/compile-iter (partial compile-expression $begin) $begin _register-offset ?args) - - (&o/$variant ?tag ?tail ?members) - (&&lux/compile-variant (partial compile-expression $begin) ?tag ?tail ?members) - - (&o/$case ?value [?pm ?bodies]) - (&&case/compile-case (partial compile-expression $begin) ?value ?pm ?bodies) - - (&o/$let _value _register _body) - (&&lux/compile-let (partial compile-expression $begin) _value _register _body) - - (&o/$record-get _value _path) - (&&lux/compile-record-get (partial compile-expression $begin) _value _path) - - (&o/$if _test _then _else) - (&&lux/compile-if (partial compile-expression $begin) _test _then _else) - - (&o/$function _register-offset ?arity ?scope ?env ?body) - (&&lambda/compile-function compile-expression &/$None ?arity ?scope ?env ?body) - - (&o/$ann ?value-ex ?type-ex) - (compile-expression $begin ?value-ex) - - (&o/$proc [?proc-category ?proc-name] ?args special-args) - (&&host/compile-host (partial compile-expression $begin) ?proc-category ?proc-name ?args special-args) - - _ - (assert false (prn-str 'compile-expression (&/adt->text syntax))) - )) - )) - -(defn init! - "(-> (List Text) Null)" - [resources-dirs ^String target-dir] - (do (reset! &&/!output-dir target-dir) +(defn init! [resources-dirs ^String target-dir] + (do (reset! &&core/!output-dir target-dir) (&¶llel/setup!) (&&io/init-libs!) - (reset! !source->last-line {}) (.mkdirs (new java.io.File target-dir)) - (let [class-loader (ClassLoader/getSystemClassLoader) - addURL (doto (.getDeclaredMethod java.net.URLClassLoader "addURL" (into-array [java.net.URL])) - (.setAccessible true))] - (doseq [^String resources-dir (&/->seq resources-dirs)] - (.invoke addURL class-loader - (to-array [(->> resources-dir (new java.io.File) .toURI .toURL)])))))) - -(defn eval! [expr] - (&/with-eval - (|do [module &/get-module-name - id &/gen-id - [file-name _ _] &/cursor - :let [class-name (str (&host/->module-class module) "/" id) - =class (doto (new ClassWriter ClassWriter/COMPUTE_MAXS) - (.visit &host/bytecode-version (+ Opcodes/ACC_PUBLIC Opcodes/ACC_SUPER) - class-name nil "java/lang/Object" nil) - (-> (.visitField (+ Opcodes/ACC_PUBLIC Opcodes/ACC_FINAL Opcodes/ACC_STATIC) &/eval-field "Ljava/lang/Object;" nil nil) - (doto (.visitEnd))) - (.visitSource file-name nil))] - _ (&/with-writer (.visitMethod =class Opcodes/ACC_STATIC "<clinit>" "()V" nil nil) - (|do [^MethodVisitor *writer* &/get-writer - :let [_ (.visitCode *writer*)] - _ (compile-expression nil expr) - :let [_ (doto *writer* - (.visitFieldInsn Opcodes/PUTSTATIC class-name &/eval-field "Ljava/lang/Object;") - (.visitInsn Opcodes/RETURN) - (.visitMaxs 0 0) - (.visitEnd))]] - (return nil))) - :let [bytecode (.toByteArray (doto =class - .visitEnd))] - _ (&&/save-class! (str id) bytecode) - loader &/loader] - (-> (.loadClass ^ClassLoader loader (str (&host-generics/->class-name module) "." id)) - (.getField &/eval-field) - (.get nil) - return)))) + (&&jvm/init! resources-dirs target-dir) + ;; (&&js/init! resources-dirs target-dir) + )) (def all-compilers - (let [compile-expression* (partial compile-expression nil)] - (&/T [(partial &&lux/compile-def compile-expression) - (partial &&lux/compile-program compile-expression*) - (partial &&host/compile-jvm-class compile-expression*) - &&host/compile-jvm-interface]))) + &&jvm/all-compilers) -(let [+field-flags+ (+ Opcodes/ACC_PUBLIC Opcodes/ACC_FINAL Opcodes/ACC_STATIC) - +datum-sig+ "Ljava/lang/Object;"] - (defn compile-module [source-dirs name] - (let [file-name (str name ".lux")] - (|do [file-content (&&io/read-file source-dirs file-name) - :let [file-hash (hash file-content) - compile-module!! (&¶llel/parallel-compilation (partial compile-module source-dirs))]] - (&/|eitherL (&&cache/load name) - (let [compiler-step (&analyser/analyse &optimizer/optimize eval! compile-module!! all-compilers)] - (|do [module-exists? (&a-module/exists? name)] - (if module-exists? - (&/fail-with-loc "[Compiler Error] Can't redefine a module!") - (|do [_ (&&cache/delete name) - _ (&a-module/create-module name file-hash) - _ (&/flag-active-module name) - :let [module-class-name (str (&host/->module-class name) "/_") - =class (doto (new ClassWriter ClassWriter/COMPUTE_MAXS) - (.visit &host/bytecode-version (+ Opcodes/ACC_PUBLIC Opcodes/ACC_SUPER) - module-class-name nil "java/lang/Object" nil) - (-> (.visitField +field-flags+ &/hash-field "I" nil file-hash) - .visitEnd) - (-> (.visitField +field-flags+ &/compiler-field "Ljava/lang/String;" nil &/compiler-version) - .visitEnd) - (.visitSource file-name nil))] - _ (if (= "lux" name) - (|do [_ &&host/compile-Function-class - _ &&host/compile-LuxRT-class] - (return nil)) - (return nil))] - (fn [state] - (|case ((&/with-writer =class - (&/exhaust% compiler-step)) - (&/set$ &/$source (&reader/from name file-content) state)) - (&/$Right ?state _) - (&/run-state (|do [:let [_ (.visitEnd =class)] - module-anns (&a-module/get-anns name) - defs &a-module/defs - imports &a-module/imports - tag-groups &&module/tag-groups - :let [def-entries (->> defs - (&/|map (fn [_def] - (|let [[?name ?alias [?def-type ?def-anns ?def-value]] _def] - (if (= "" ?alias) - (str ?name &&/datum-separator (&&&type/serialize-type ?def-type) &&/datum-separator (&&&ann/serialize-anns ?def-anns)) - (str ?name &&/datum-separator ?alias))))) - (&/|interpose &&/entry-separator) - (&/fold str "")) - import-entries (->> imports - (&/|map (fn [import] - (|let [[_module _hash] import] - (str _module &&/datum-separator _hash)))) - (&/|interpose &&/entry-separator) - (&/fold str "")) - tag-entries (->> tag-groups - (&/|map (fn [group] - (|let [[type tags] group] - (->> tags - (&/|interpose &&/datum-separator) - (&/fold str "") - (str type &&/datum-separator))))) - (&/|interpose &&/entry-separator) - (&/fold str "")) - module-descriptor (->> (&/|list import-entries - tag-entries - (&&&ann/serialize-anns module-anns) - def-entries) - (&/|interpose &&/section-separator) - (&/fold str ""))] - _ (&/flag-compiled-module name) - _ (&&/save-class! &/module-class-name (.toByteArray =class)) - _ (&&/write-module-descriptor! name module-descriptor)] - (return file-hash)) - ?state) - - (&/$Left ?message) - (&/fail* ?message)))))))) - ) - ))) +(defn eval! [expr] + (&&jvm/eval! expr)) -(let [!err! *err*] - (defn compile-program [mode program-module resources-dir source-dirs target-dir] - (do (init! resources-dir target-dir) - (let [m-action (|do [_ (&&cache/pre-load-cache! source-dirs) - _ (compile-module source-dirs "lux")] - (compile-module source-dirs program-module))] - (|case (m-action (&/init-state mode)) - (&/$Right ?state _) - (do (println "Compilation complete!") - (&&cache/clean ?state)) +(defn compile-module [source-dirs name] + (&&jvm/compile-module source-dirs name)) - (&/$Left ?message) - (binding [*out* !err!] - (do (println (str "Compilation failed:\n" ?message)) - (flush) - (System/exit 1)))))))) +(defn compile-program [mode program-module resources-dir source-dirs target-dir] + (init! resources-dir target-dir) + (&&jvm/compile-program mode program-module resources-dir source-dirs target-dir) + ;; (&&js/compile-program mode program-module resources-dir source-dirs target-dir) + ) diff --git a/luxc/src/lux/compiler/core.clj b/luxc/src/lux/compiler/core.clj new file mode 100644 index 000000000..4779c3c28 --- /dev/null +++ b/luxc/src/lux/compiler/core.clj @@ -0,0 +1,82 @@ +(ns lux.compiler.core + (:require (clojure [template :refer [do-template]] + [string :as string]) + [clojure.java.io :as io] + [clojure.core.match :as M :refer [matchv]] + clojure.core.match.array + (lux [base :as & :refer [|let |do return* return fail*]] + [type :as &type] + [host :as &host]) + (lux.analyser [base :as &a] + [module :as &a-module]) + (lux.compiler.cache [type :as &&&type] + [ann :as &&&ann])) + (:import (java.io File + BufferedOutputStream + FileOutputStream))) + +;; [Constants] +(def !output-dir (atom nil)) + +(def ^:const section-separator (->> 29 char str)) +(def ^:const datum-separator (->> 31 char str)) +(def ^:const entry-separator (->> 30 char str)) + +;; [Utils] +(defn write-file [^String file-name ^bytes data] + (do (assert (not (.exists (File. file-name))) (str "Can't overwrite file: " file-name)) + (with-open [stream (BufferedOutputStream. (FileOutputStream. file-name))] + (.write stream data) + (.flush stream)))) + +;; [Exports] +(def ^String lux-module-descriptor-name "lux_module_descriptor") + +(defn write-module-descriptor! [^String name ^String descriptor] + (|do [_ (return nil) + :let [lmd-dir (str @!output-dir java.io.File/separator (.replace name "/" java.io.File/separator)) + _ (.mkdirs (File. lmd-dir)) + _ (write-file (str lmd-dir java.io.File/separator lux-module-descriptor-name) (.getBytes descriptor java.nio.charset.StandardCharsets/UTF_8))]] + (return nil))) + +(defn read-module-descriptor! [^String name] + (|do [_ (return nil)] + (return (slurp (str @!output-dir java.io.File/separator (.replace name "/" java.io.File/separator) java.io.File/separator lux-module-descriptor-name) + :encoding "UTF-8")))) + +(def generate-module-descriptor + (|do [module-name &/get-module-name + module-anns (&a-module/get-anns module-name) + defs &a-module/defs + imports &a-module/imports + tag-groups &a-module/tag-groups + :let [def-entries (->> defs + (&/|map (fn [_def] + (|let [[?name ?alias [?def-type ?def-anns ?def-value]] _def] + (if (= "" ?alias) + (str ?name datum-separator (&&&type/serialize-type ?def-type) datum-separator (&&&ann/serialize-anns ?def-anns)) + (str ?name datum-separator ?alias))))) + (&/|interpose entry-separator) + (&/fold str "")) + import-entries (->> imports + (&/|map (fn [import] + (|let [[_module _hash] import] + (str _module datum-separator _hash)))) + (&/|interpose entry-separator) + (&/fold str "")) + tag-entries (->> tag-groups + (&/|map (fn [group] + (|let [[type tags] group] + (->> tags + (&/|interpose datum-separator) + (&/fold str "") + (str type datum-separator))))) + (&/|interpose entry-separator) + (&/fold str "")) + module-descriptor (->> (&/|list import-entries + tag-entries + (&&&ann/serialize-anns module-anns) + def-entries) + (&/|interpose section-separator) + (&/fold str ""))]] + (return module-descriptor))) diff --git a/luxc/src/lux/compiler/io.clj b/luxc/src/lux/compiler/io.clj index 3ee19988f..82b80f624 100644 --- a/luxc/src/lux/compiler/io.clj +++ b/luxc/src/lux/compiler/io.clj @@ -1,6 +1,6 @@ (ns lux.compiler.io (:require (lux [base :as & :refer [|case |let |do return* return fail*]]) - (lux.compiler [base :as &&]) + (lux.compiler.jvm [base :as &&]) [lux.lib.loader :as &lib])) ;; [Utils] diff --git a/luxc/src/lux/compiler/jvm.clj b/luxc/src/lux/compiler/jvm.clj new file mode 100644 index 000000000..5d787f5cd --- /dev/null +++ b/luxc/src/lux/compiler/jvm.clj @@ -0,0 +1,228 @@ +(ns lux.compiler.jvm + (:refer-clojure :exclude [compile]) + (:require (clojure [string :as string] + [set :as set] + [template :refer [do-template]]) + clojure.core.match + clojure.core.match.array + (lux [base :as & :refer [|let |do return* return |case]] + [type :as &type] + [reader :as &reader] + [lexer :as &lexer] + [parser :as &parser] + [analyser :as &analyser] + [optimizer :as &optimizer] + [host :as &host]) + [lux.host.generics :as &host-generics] + [lux.optimizer :as &o] + [lux.analyser.base :as &a] + [lux.analyser.module :as &a-module] + (lux.compiler [core :as &&core] + [io :as &&io] + [parallel :as &¶llel]) + (lux.compiler.jvm [base :as &&] + [cache :as &&cache] + [lux :as &&lux] + [host :as &&host] + [case :as &&case] + [lambda :as &&lambda])) + (:import (org.objectweb.asm Opcodes + Label + ClassWriter + MethodVisitor))) + +;; [Resources] +(def ^:private !source->last-line (atom nil)) + +(defn ^:private compile-expression [$begin syntax] + (|let [[[?type [_file-name _line _]] ?form] syntax] + (|do [^MethodVisitor *writer* &/get-writer + :let [debug-label (new Label) + _ (when (not= _line (get @!source->last-line _file-name)) + (doto *writer* + (.visitLabel debug-label) + (.visitLineNumber (int _line) debug-label)) + (swap! !source->last-line assoc _file-name _line))]] + (|case ?form + (&o/$bool ?value) + (&&lux/compile-bool ?value) + + (&o/$nat ?value) + (&&lux/compile-nat ?value) + + (&o/$int ?value) + (&&lux/compile-int ?value) + + (&o/$deg ?value) + (&&lux/compile-deg ?value) + + (&o/$real ?value) + (&&lux/compile-real ?value) + + (&o/$char ?value) + (&&lux/compile-char ?value) + + (&o/$text ?value) + (&&lux/compile-text ?value) + + (&o/$tuple ?elems) + (&&lux/compile-tuple (partial compile-expression $begin) ?elems) + + (&o/$var (&/$Local ?idx)) + (&&lux/compile-local (partial compile-expression $begin) ?idx) + + (&o/$captured ?scope ?captured-id ?source) + (&&lux/compile-captured (partial compile-expression $begin) ?scope ?captured-id ?source) + + (&o/$var (&/$Global ?owner-class ?name)) + (&&lux/compile-global (partial compile-expression $begin) ?owner-class ?name) + + (&o/$apply ?fn ?args) + (&&lux/compile-apply (partial compile-expression $begin) ?fn ?args) + + (&o/$loop _register-offset _inits _body) + (&&lux/compile-loop compile-expression _register-offset _inits _body) + + (&o/$iter _register-offset ?args) + (&&lux/compile-iter (partial compile-expression $begin) $begin _register-offset ?args) + + (&o/$variant ?tag ?tail ?members) + (&&lux/compile-variant (partial compile-expression $begin) ?tag ?tail ?members) + + (&o/$case ?value [?pm ?bodies]) + (&&case/compile-case (partial compile-expression $begin) ?value ?pm ?bodies) + + (&o/$let _value _register _body) + (&&lux/compile-let (partial compile-expression $begin) _value _register _body) + + (&o/$record-get _value _path) + (&&lux/compile-record-get (partial compile-expression $begin) _value _path) + + (&o/$if _test _then _else) + (&&lux/compile-if (partial compile-expression $begin) _test _then _else) + + (&o/$function _register-offset ?arity ?scope ?env ?body) + (&&lambda/compile-function compile-expression &/$None ?arity ?scope ?env ?body) + + (&o/$ann ?value-ex ?type-ex) + (compile-expression $begin ?value-ex) + + (&o/$proc [?proc-category ?proc-name] ?args special-args) + (&&host/compile-host (partial compile-expression $begin) ?proc-category ?proc-name ?args special-args) + + _ + (assert false (prn-str 'compile-expression (&/adt->text syntax))) + )) + )) + +(defn init! + "(-> (List Text) Null)" + [resources-dirs ^String target-dir] + (do (reset! !source->last-line {}) + (let [class-loader (ClassLoader/getSystemClassLoader) + addURL (doto (.getDeclaredMethod java.net.URLClassLoader "addURL" (into-array [java.net.URL])) + (.setAccessible true))] + (doseq [^String resources-dir (&/->seq resources-dirs)] + (.invoke addURL class-loader + (to-array [(->> resources-dir (new java.io.File) .toURI .toURL)])))))) + +(defn eval! [expr] + (&/with-eval + (|do [module &/get-module-name + id &/gen-id + [file-name _ _] &/cursor + :let [class-name (str (&host/->module-class module) "/" id) + =class (doto (new ClassWriter ClassWriter/COMPUTE_MAXS) + (.visit &host/bytecode-version (+ Opcodes/ACC_PUBLIC Opcodes/ACC_SUPER) + class-name nil "java/lang/Object" nil) + (-> (.visitField (+ Opcodes/ACC_PUBLIC Opcodes/ACC_FINAL Opcodes/ACC_STATIC) &/eval-field "Ljava/lang/Object;" nil nil) + (doto (.visitEnd))) + (.visitSource file-name nil))] + _ (&/with-writer (.visitMethod =class Opcodes/ACC_STATIC "<clinit>" "()V" nil nil) + (|do [^MethodVisitor *writer* &/get-writer + :let [_ (.visitCode *writer*)] + _ (compile-expression nil expr) + :let [_ (doto *writer* + (.visitFieldInsn Opcodes/PUTSTATIC class-name &/eval-field "Ljava/lang/Object;") + (.visitInsn Opcodes/RETURN) + (.visitMaxs 0 0) + (.visitEnd))]] + (return nil))) + :let [bytecode (.toByteArray (doto =class + .visitEnd))] + _ (&&/save-class! (str id) bytecode) + loader &/loader] + (-> (.loadClass ^ClassLoader loader (str (&host-generics/->class-name module) "." id)) + (.getField &/eval-field) + (.get nil) + return)))) + +(def all-compilers + (let [compile-expression* (partial compile-expression nil)] + (&/T [(partial &&lux/compile-def compile-expression) + (partial &&lux/compile-program compile-expression*) + (partial &&host/compile-jvm-class compile-expression*) + &&host/compile-jvm-interface]))) + +(let [+field-flags+ (+ Opcodes/ACC_PUBLIC Opcodes/ACC_FINAL Opcodes/ACC_STATIC) + +datum-sig+ "Ljava/lang/Object;"] + (defn compile-module [source-dirs name] + (let [file-name (str name ".lux")] + (|do [file-content (&&io/read-file source-dirs file-name) + :let [file-hash (hash file-content) + compile-module!! (&¶llel/parallel-compilation (partial compile-module source-dirs))]] + (&/|eitherL (&&cache/load name) + (let [compiler-step (&analyser/analyse &optimizer/optimize eval! compile-module!! all-compilers)] + (|do [module-exists? (&a-module/exists? name)] + (if module-exists? + (&/fail-with-loc "[Compiler Error] Can't re-define a module!") + (|do [_ (&&cache/delete name) + _ (&a-module/create-module name file-hash) + _ (&/flag-active-module name) + :let [module-class-name (str (&host/->module-class name) "/_") + =class (doto (new ClassWriter ClassWriter/COMPUTE_MAXS) + (.visit &host/bytecode-version (+ Opcodes/ACC_PUBLIC Opcodes/ACC_SUPER) + module-class-name nil "java/lang/Object" nil) + (-> (.visitField +field-flags+ &/hash-field "I" nil file-hash) + .visitEnd) + (-> (.visitField +field-flags+ &/compiler-field "Ljava/lang/String;" nil &/compiler-version) + .visitEnd) + (.visitSource file-name nil))] + _ (if (= "lux" name) + (|do [_ &&host/compile-Function-class + _ &&host/compile-LuxRT-class] + (return nil)) + (return nil))] + (fn [state] + (|case ((&/with-writer =class + (&/exhaust% compiler-step)) + (&/set$ &/$source (&reader/from name file-content) state)) + (&/$Right ?state _) + (&/run-state (|do [:let [_ (.visitEnd =class)] + _ (&/flag-compiled-module name) + _ (&&/save-class! &/module-class-name (.toByteArray =class)) + module-descriptor &&core/generate-module-descriptor + _ (&&core/write-module-descriptor! name module-descriptor)] + (return file-hash)) + ?state) + + (&/$Left ?message) + (&/fail* ?message)))))))) + ) + ))) + +(let [!err! *err*] + (defn compile-program [mode program-module resources-dir source-dirs target-dir] + (let [m-action (|do [_ (&&cache/pre-load-cache! source-dirs) + _ (compile-module source-dirs "lux")] + (compile-module source-dirs program-module))] + (|case (m-action (&/init-state mode)) + (&/$Right ?state _) + (do (println "Compilation complete!") + (&&cache/clean ?state)) + + (&/$Left ?message) + (binding [*out* !err!] + (do (println (str "Compilation failed:\n" ?message)) + (flush) + (System/exit 1))))))) diff --git a/luxc/src/lux/compiler/base.clj b/luxc/src/lux/compiler/jvm/base.clj index e57fc1e2b..268b293e9 100644 --- a/luxc/src/lux/compiler/base.clj +++ b/luxc/src/lux/compiler/jvm/base.clj @@ -1,4 +1,4 @@ -(ns lux.compiler.base +(ns lux.compiler.jvm.base (:require (clojure [template :refer [do-template]] [string :as string]) [clojure.java.io :as io] @@ -9,7 +9,8 @@ [host :as &host]) (lux.analyser [base :as &a] [module :as &a-module]) - [lux.host.generics :as &host-generics]) + [lux.host.generics :as &host-generics] + [lux.compiler.core :as &&]) (:import (org.objectweb.asm Opcodes Label ClassWriter @@ -20,8 +21,6 @@ (java.lang.reflect Field))) ;; [Constants] -(def !output-dir (atom nil)) - (def ^:const ^String function-class "lux/Function") (def ^:const ^String lux-utils-class "lux/LuxRT") (def ^:const ^String unit-tag-field "unit_tag") @@ -37,27 +36,17 @@ (def ^:const arity-field "_arity_") (def ^:const partials-field "_partials_") -(def ^:const section-separator (->> 29 char str)) -(def ^:const datum-separator (->> 31 char str)) -(def ^:const entry-separator (->> 30 char str)) - ;; [Utils] -(defn ^:private write-file [^String file-name ^bytes data] - (do (assert (not (.exists (File. file-name))) (str "Can't overwrite file: " file-name)) - (with-open [stream (BufferedOutputStream. (FileOutputStream. file-name))] - (.write stream data) - (.flush stream)))) - (defn ^:private write-output [module name data] (let [^String module* (&host/->module-class module) - module-dir (str @!output-dir java.io.File/separator (.replace module* "/" java.io.File/separator))] + module-dir (str @&&/!output-dir java.io.File/separator (.replace module* "/" java.io.File/separator))] (.mkdirs (File. module-dir)) - (write-file (str module-dir java.io.File/separator name ".class") data))) + (&&/write-file (str module-dir java.io.File/separator name ".class") data))) (defn class-exists? [^String module ^String class-name] "(-> Text Text (IO Bool))" (|do [_ (return nil) - :let [full-path (str @!output-dir java.io.File/separator module java.io.File/separator class-name ".class") + :let [full-path (str @&&/!output-dir java.io.File/separator module java.io.File/separator class-name ".class") exists? (.exists (File. full-path))]] (return exists?))) @@ -78,20 +67,6 @@ _ (load-class! loader real-name)]] (return nil))) -(def ^String lux-module-descriptor-name "lux_module_descriptor") - -(defn write-module-descriptor! [^String name ^String descriptor] - (|do [_ (return nil) - :let [lmd-dir (str @!output-dir java.io.File/separator (.replace name "/" java.io.File/separator)) - _ (.mkdirs (File. lmd-dir)) - _ (write-file (str lmd-dir java.io.File/separator lux-module-descriptor-name) (.getBytes descriptor java.nio.charset.StandardCharsets/UTF_8))]] - (return nil))) - -(defn read-module-descriptor! [^String name] - (|do [_ (return nil)] - (return (slurp (str @!output-dir java.io.File/separator (.replace name "/" java.io.File/separator) java.io.File/separator lux-module-descriptor-name) - :encoding "UTF-8")))) - (do-template [<wrap-name> <unwrap-name> <class> <unwrap-method> <prim> <dup>] (do (defn <wrap-name> [^MethodVisitor writer] (doto writer diff --git a/luxc/src/lux/compiler/cache.clj b/luxc/src/lux/compiler/jvm/cache.clj index 8ca319d66..1746514bc 100644 --- a/luxc/src/lux/compiler/cache.clj +++ b/luxc/src/lux/compiler/jvm/cache.clj @@ -1,4 +1,4 @@ -(ns lux.compiler.cache +(ns lux.compiler.jvm.cache (:refer-clojure :exclude [load]) (:require [clojure.string :as string] [clojure.java.io :as io] @@ -11,10 +11,11 @@ (lux.analyser [base :as &a] [module :as &a-module] [meta :as &a-meta]) - (lux.compiler [base :as &&] + (lux.compiler [core :as &&core] [io :as &&io]) (lux.compiler.cache [type :as &&&type] - [ann :as &&&ann])) + [ann :as &&&ann]) + (lux.compiler.jvm [base :as &&])) (:import (java.io File BufferedOutputStream FileOutputStream) @@ -44,7 +45,7 @@ (defn cached? [module] "(-> Text Bool)" - (.exists (new File (str @&&/!output-dir + (.exists (new File (str @&&core/!output-dir java.io.File/separator (.replace ^String (&host/->module-class module) "/" java.io.File/separator) java.io.File/separator @@ -53,7 +54,7 @@ (defn delete [module] "(-> Text (Lux Null))" (fn [state] - (do (clean-file (new File (str @&&/!output-dir + (do (clean-file (new File (str @&&core/!output-dir java.io.File/separator (.replace ^String (&host/->module-class module) "/" java.io.File/separator)))) (return* state nil)))) @@ -71,9 +72,9 @@ (defn clean [state] "(-> Compiler Null)" (let [needed-modules (->> state (&/get$ &/$modules) &/|keys &/->seq set) - output-dir-prefix (str (.getAbsolutePath (new File ^String @&&/!output-dir)) java.io.File/separator) + output-dir-prefix (str (.getAbsolutePath (new File ^String @&&core/!output-dir)) java.io.File/separator) outdated? #(->> % (contains? needed-modules) not) - outdated-modules (->> (new File ^String @&&/!output-dir) + outdated-modules (->> (new File ^String @&&core/!output-dir) .listFiles (filter #(.isDirectory ^File %)) (map module-dirs) doall (apply concat) (map (fn [^File dir-file] @@ -113,10 +114,10 @@ (if (= "" tags-section) &/$Nil (-> tags-section - (.split &&/entry-separator) + (.split &&core/entry-separator) seq (->> (map (fn [^String _group] - (let [[_type & _tags] (.split _group &&/datum-separator)] + (let [[_type & _tags] (.split _group &&core/datum-separator)] (&/T [_type (->> _tags seq &/->list)]))))) &/->list))) @@ -126,7 +127,7 @@ (&a-module/declare-tags module _tags was-exported? =type)))) (defn ^:private process-def-entry [loader module ^String _def-entry] - (let [parts (.split _def-entry &&/datum-separator)] + (let [parts (.split _def-entry &&core/datum-separator)] (case (alength parts) 2 (let [[_name _alias] parts [_ __module __name] (re-find #"^(.*);(.*)$" _alias) @@ -156,13 +157,13 @@ (return nil))) (defn ^:private process-module [pre-load! source-dirs cache-table module-name module-hash loader] - (|do [^String descriptor (&&/read-module-descriptor! module-name) - :let [[imports-section tags-section module-anns-section defs-section] (.split descriptor &&/section-separator) - imports (let [imports (vec (.split ^String imports-section &&/entry-separator)) + (|do [^String descriptor (&&core/read-module-descriptor! module-name) + :let [[imports-section tags-section module-anns-section defs-section] (.split descriptor &&core/section-separator) + imports (let [imports (vec (.split ^String imports-section &&core/entry-separator)) imports (if (= [""] imports) &/$Nil (&/->list imports))] - (&/|map #(.split ^String % &&/datum-separator 2) imports))] + (&/|map #(.split ^String % &&core/datum-separator 2) imports))] cache-table* (&/fold% (fn [cache-table* _import] (|do [:let [[_module _hash] _import] file-content (&&io/read-file source-dirs (str _module ".lux")) @@ -176,7 +177,7 @@ imports) (let [tag-groups (parse-tag-groups tags-section) module-anns (&&&ann/deserialize-anns module-anns-section) - def-entries (let [def-entries (vec (.split ^String defs-section &&/entry-separator))] + def-entries (let [def-entries (vec (.split ^String defs-section &&core/entry-separator))] (if (= [""] def-entries) &/$Nil (&/->list def-entries)))] @@ -198,7 +199,7 @@ (list))) (defn ^:private enumerate-cached-modules! [] - (let [output-dir (new File ^String @&&/!output-dir) + (let [output-dir (new File ^String @&&core/!output-dir) prefix-to-subtract (inc (.length (.getAbsolutePath output-dir)))] (->> output-dir enumerate-cached-modules!* @@ -219,7 +220,7 @@ (|do [loader &/loader !classes &/classes :let [module* (&host-generics/->class-name module) - module-path (str @&&/!output-dir java.io.File/separator module) + module-path (str @&&core/!output-dir java.io.File/separator module) class-name (str module* "." &/module-class-name) ^Class module-class (do (swap! !classes assoc class-name (read-file (new File (str module-path java.io.File/separator module-class-file)))) (&&/load-class! loader class-name)) diff --git a/luxc/src/lux/compiler/case.clj b/luxc/src/lux/compiler/jvm/case.clj index aac3b6c98..da8d8d0a9 100644 --- a/luxc/src/lux/compiler/case.clj +++ b/luxc/src/lux/compiler/jvm/case.clj @@ -1,4 +1,4 @@ -(ns lux.compiler.case +(ns lux.compiler.jvm.case (:require (clojure [set :as set] [template :refer [do-template]]) clojure.core.match @@ -11,7 +11,7 @@ [host :as &host] [optimizer :as &o]) [lux.analyser.case :as &a-case] - [lux.compiler.base :as &&]) + [lux.compiler.jvm.base :as &&]) (:import (org.objectweb.asm Opcodes Label ClassWriter diff --git a/luxc/src/lux/compiler/host.clj b/luxc/src/lux/compiler/jvm/host.clj index f0249f3d3..34a5a2bb7 100644 --- a/luxc/src/lux/compiler/host.clj +++ b/luxc/src/lux/compiler/jvm/host.clj @@ -1,4 +1,4 @@ -(ns lux.compiler.host +(ns lux.compiler.jvm.host (:require (clojure [string :as string] [set :as set] [template :refer [do-template]]) @@ -14,7 +14,7 @@ [lux.type.host :as &host-type] [lux.host.generics :as &host-generics] [lux.analyser.base :as &a] - [lux.compiler.base :as &&]) + [lux.compiler.jvm.base :as &&]) (:import (org.objectweb.asm Opcodes Label ClassWriter diff --git a/luxc/src/lux/compiler/lambda.clj b/luxc/src/lux/compiler/jvm/lambda.clj index 006476bef..87d977012 100644 --- a/luxc/src/lux/compiler/lambda.clj +++ b/luxc/src/lux/compiler/jvm/lambda.clj @@ -1,4 +1,4 @@ -(ns lux.compiler.lambda +(ns lux.compiler.jvm.lambda (:require (clojure [string :as string] [set :as set] [template :refer [do-template]]) @@ -13,7 +13,7 @@ [optimizer :as &o]) [lux.host.generics :as &host-generics] [lux.analyser.base :as &a] - (lux.compiler [base :as &&])) + (lux.compiler.jvm [base :as &&])) (:import (org.objectweb.asm Opcodes Label ClassWriter diff --git a/luxc/src/lux/compiler/lux.clj b/luxc/src/lux/compiler/jvm/lux.clj index 36d923e60..591e490c4 100644 --- a/luxc/src/lux/compiler/lux.clj +++ b/luxc/src/lux/compiler/jvm/lux.clj @@ -1,4 +1,4 @@ -(ns lux.compiler.lux +(ns lux.compiler.jvm.lux (:require (clojure [string :as string] [set :as set] [template :refer [do-template]]) @@ -15,8 +15,8 @@ (lux.analyser [base :as &a] [module :as &a-module] [meta :as &a-meta]) - (lux.compiler [base :as &&] - [lambda :as &&lambda])) + (lux.compiler.jvm [base :as &&] + [lambda :as &&lambda])) (:import (org.objectweb.asm Opcodes Label ClassWriter diff --git a/luxc/src/lux/compiler/module.clj b/luxc/src/lux/compiler/module.clj deleted file mode 100644 index 9ca4e040b..000000000 --- a/luxc/src/lux/compiler/module.clj +++ /dev/null @@ -1,23 +0,0 @@ -(ns lux.compiler.module - (:require (clojure [string :as string] - [set :as set] - [template :refer [do-template]]) - clojure.core.match - clojure.core.match.array - (lux [base :as & :refer [|let |do return* return |case]] - [type :as &type]) - [lux.analyser.module :as &module])) - -;; [Exports] -(def tag-groups - "(Lux (List (, Text (List Text))))" - (|do [module &/get-current-module] - (return (&/|map (fn [pair] - (|case pair - [name [tags exported? _]] - (&/T [name (&/|map (fn [tag] - (|let [[t-prefix t-name] tag] - t-name)) - tags)]))) - (&/get$ &module/$types module))) - )) diff --git a/luxc/src/lux/repl.clj b/luxc/src/lux/repl.clj index 22c2f47d2..7562aaf70 100644 --- a/luxc/src/lux/repl.clj +++ b/luxc/src/lux/repl.clj @@ -6,7 +6,7 @@ [analyser :as &analyser] [optimizer :as &optimizer] [compiler :as &compiler]) - [lux.compiler.cache :as &cache] + [lux.compiler.jvm.cache :as &cache] [lux.analyser.base :as &a-base] [lux.analyser.lux :as &a-lux] [lux.analyser.module :as &module]) |