diff options
Diffstat (limited to 'src/lux/compiler')
-rw-r--r-- | src/lux/compiler/case.clj | 568 | ||||
-rw-r--r-- | src/lux/compiler/host.clj | 194 | ||||
-rw-r--r-- | src/lux/compiler/lambda.clj | 176 |
3 files changed, 938 insertions, 0 deletions
diff --git a/src/lux/compiler/case.clj b/src/lux/compiler/case.clj new file mode 100644 index 000000000..8f35ec2c0 --- /dev/null +++ b/src/lux/compiler/case.clj @@ -0,0 +1,568 @@ + +(let [+tag-sig+ (->type-signature "java.lang.String") + variant-class* (->class +variant-class+) + tuple-class* (->class +tuple-class+) + +variant-field-sig+ (->type-signature "java.lang.Object") + oclass (->class "java.lang.Object") + equals-sig (str "(" (->type-signature "java.lang.Object") ")Z")] + (defn ^:private compile-decision-tree [writer mappings default-label decision-tree] + (match decision-tree + [::test-bool ?pairs] + (compile-compare-bools writer mappings default-label ?pairs) + + [::test-int ?pairs] + (compile-compare-ints writer mappings default-label ?pairs) + + [::test-real ?pairs] + (compile-compare-reals writer mappings default-label ?pairs) + + [::test-char ?pairs] + (compile-compare-chars writer mappings default-label ?pairs) + + [::test-text ?pairs] + (compile-compare-texts writer mappings default-label ?pairs) + + [::store ?idx $body] + (doto writer + (.visitVarInsn Opcodes/ASTORE ?idx) + (.visitJumpInsn Opcodes/GOTO (get mappings $body))) + + [::test-tuple ?branches ?cases] + (let [[_ ?subcases] (first ?cases) + arity (-> ?subcases first (nth 2) count) + tuple-class** (str tuple-class* arity)] + (doto writer + ;; object + (.visitTypeInsn Opcodes/CHECKCAST tuple-class**) ;; tuple + (do (doseq [subcase ?subcases + :let [next-subcase (new Label)]] + (match subcase + [::subcase $body ?subseq] + (do (doseq [[?subpart ?subidx] (map vector ?subseq (range (count ?subseq))) + :let [sub-next-elem (new Label)]] + (doto writer + (.visitInsn Opcodes/DUP) ;; tuple, tuple + (.visitFieldInsn Opcodes/GETFIELD tuple-class** (str +partial-prefix+ ?subidx) +variant-field-sig+) ;; tuple, object + (compile-decision-tree (assoc mappings $body sub-next-elem) next-subcase ?subpart) ;; tuple + (.visitLabel sub-next-elem))) + (doto writer + (.visitInsn Opcodes/POP) + (.visitJumpInsn Opcodes/GOTO (get mappings $body)) + (.visitLabel next-subcase))) + ))) + (.visitInsn Opcodes/POP) ;; -> + (.visitJumpInsn Opcodes/GOTO default-label))) + + [::test-variant ?branches ?cases] + (doto writer + ;; object + (.visitTypeInsn Opcodes/CHECKCAST variant-class*) ;; variant + (.visitInsn Opcodes/DUP) ;; variant, variant + (.visitFieldInsn Opcodes/GETFIELD variant-class* "tag" +tag-sig+) ;; variant, tag + (-> (doto (.visitInsn Opcodes/DUP) ;; variant, tag, tag + (.visitLdcInsn ?tag) ;; variant, tag, tag, text + (.visitMethodInsn Opcodes/INVOKEVIRTUAL oclass "equals" equals-sig) ;; variant, tag, B + (.visitJumpInsn Opcodes/IFEQ tag-else-label) ;; variant, tag + (.visitInsn Opcodes/POP) ;; variant + (do (let [arity (-> ?subcases first (nth 2) count) + variant-class** (str variant-class* arity)] + (.visitTypeInsn writer Opcodes/CHECKCAST variant-class**) ;; variantN + (doseq [subcase ?subcases + :let [next-subcase (new Label)]] + (match subcase + [::subcase $body ?subseq] + (do (doseq [[?subpart ?subidx] (map vector ?subseq (range (count ?subseq))) + :let [sub-next-elem (new Label)]] + (doto writer + (.visitInsn Opcodes/DUP) ;; variant, variant + (.visitFieldInsn Opcodes/GETFIELD variant-class** (str +partial-prefix+ ?subidx) +variant-field-sig+) ;; variant, object + (compile-decision-tree (assoc mappings $body sub-next-elem) next-subcase ?subpart) ;; variant + (.visitLabel sub-next-elem))) + (doto writer + (.visitInsn Opcodes/POP) + (.visitJumpInsn Opcodes/GOTO (get mappings $body)) + (.visitLabel next-subcase))) + )) + )) + (.visitInsn Opcodes/POP) ;; -> + (.visitJumpInsn Opcodes/GOTO default-label) + ;; variant, tag -> + (.visitLabel tag-else-label)) + (->> (doseq [[?tag ?subcases] ?cases + :let [tag-else-label (new Label)]]))) + ;; variant, tag -> + (.visitInsn Opcodes/POP) ;; variant -> + (.visitInsn Opcodes/POP) ;; -> + (.visitJumpInsn Opcodes/GOTO default-label))) + )) + +(defn ^:private map-branches [idx mappings patterns] + (reduce (fn [[idx mappings patterns*] [test body]] + [(inc idx) + (assoc mappings idx body) + (cons [test idx] patterns*)]) + [idx mappings (list)] + patterns)) + +(defn ^:private map-bodies [pm-struct] + (match pm-struct + [::BoolPM ?patterns ?defaults] + (let [[idx mappings patterns*] (map-branches 0 {} ?patterns) + [_ mappings* defaults*] (map-branches idx mappings ?defaults)] + [mappings* [::BoolPM patterns* defaults*]]) + + [::IntPM ?patterns ?defaults] + (let [[idx mappings patterns*] (map-branches 0 {} ?patterns) + [_ mappings* defaults*] (map-branches idx mappings ?defaults)] + [mappings* [::IntPM patterns* defaults*]]) + + [::RealPM ?patterns ?defaults] + (let [[idx mappings patterns*] (map-branches 0 {} ?patterns) + [_ mappings* defaults*] (map-branches idx mappings ?defaults)] + [mappings* [::RealPM patterns* defaults*]]) + + [::CharPM ?patterns ?defaults] + (let [[idx mappings patterns*] (map-branches 0 {} ?patterns) + [_ mappings* defaults*] (map-branches idx mappings ?defaults)] + [mappings* [::CharPM patterns* defaults*]]) + + [::TextPM ?patterns ?defaults] + (let [[idx mappings patterns*] (map-branches 0 {} ?patterns) + [_ mappings* defaults*] (map-branches idx mappings ?defaults)] + [mappings* [::TextPM patterns* defaults*]]) + + [::TuplePM ?num-elems ?patterns ?defaults] + (let [[idx mappings patterns*] (map-branches 0 {} ?patterns) + [_ mappings* defaults*] (map-branches idx mappings ?defaults)] + [mappings* [::TuplePM ?num-elems patterns* defaults*]]) + + [::VariantPM ?tags ?patterns ?defaults] + (let [[idx mappings patterns*] (map-branches 0 {} ?patterns) + [_ mappings* defaults*] (map-branches idx mappings ?defaults)] + [mappings* [::VariantPM ?tags patterns* defaults*]]) + + [::?PM ?defaults] + (let [[_ mappings defaults*] (map-branches 0 {} ?defaults)] + [mappings [::?PM defaults*]]))) + +(defn ^:private get-default [pm-struct] + (match pm-struct + [::BoolPM ?patterns ?defaults] + (first ?defaults) + + [::IntPM ?patterns ?defaults] + (first ?defaults) + + [::RealPM ?patterns ?defaults] + (first ?defaults) + + [::CharPM ?patterns ?defaults] + (first ?defaults) + + [::TextPM ?patterns ?defaults] + (first ?defaults) + + [::TuplePM ?num-elems ?patterns ?defaults] + (first ?defaults) + + [::VariantPM ?tags ?patterns ?defaults] + (first ?defaults) + + [::?PM ?defaults] + (first ?defaults) + )) + +(do-template [<name> <wrapper-class> <value-method> <method-sig>] + (defn <name> [writer mappings $default ?patterns] + (doseq [[?token $body] ?patterns + :let [$else (new Label)]] + (doto writer + (.visitMethodInsn Opcodes/INVOKEVIRTUAL (->class <wrapper-class>) <value-method> <method-sig>) + (.visitLdcInsn ?token) + (.visitJumpInsn Opcodes/IF_ICMPNE $else) + (.visitInsn Opcodes/POP) + (.visitJumpInsn Opcodes/GOTO (get mappings $body)) + (.visitLabel $else))) + (doto writer + (.visitInsn Opcodes/POP) + (.visitJumpInsn Opcodes/GOTO $default))) + + ^:private compile-bool-pm "java.lang.Boolean" "booleanValue" "()Z" + ^:private compile-char-pm "java.lang.Character" "charValue" "()C" + ) + +(do-template [<name> <wrapper-class> <value-method> <method-sig> <cmp-op>] + (defn <name> [writer mappings $default ?patterns] + (doseq [[?token $body] ?patterns + :let [$else (new Label)]] + (doto writer + (.visitMethodInsn Opcodes/INVOKEVIRTUAL (->class <wrapper-class>) <value-method> <method-sig>) + (.visitLdcInsn ?token) + (.visitInsn <cmp-op>) + (.visitJumpInsn Opcodes/IFNE $else) + (.visitInsn Opcodes/POP) + (.visitJumpInsn Opcodes/GOTO (get mappings $body)) + (.visitLabel $else))) + (doto writer + (.visitInsn Opcodes/POP) + (.visitJumpInsn Opcodes/GOTO $default))) + + ^:private compile-int-pm "java.lang.Long" "longValue" "()J" Opcodes/LCMP + ^:private compile-real-pm "java.lang.Double" "doubleValue" "()D" Opcodes/DCMPL + ) + +(defn ^:private compile-text-pm [writer mappings $default ?patterns] + (doseq [[?token $body] ?patterns + :let [$else (new Label)]] + (doto writer + (.visitInsn Opcodes/DUP) + (.visitLdcInsn ?token) + (.visitMethodInsn Opcodes/INVOKEVIRTUAL (->class "java.lang.Object") "equals" (str "(" (->type-signature "java.lang.Object") ")Z")) + (.visitJumpInsn Opcodes/IFEQ $else) + (.visitInsn Opcodes/POP) + (.visitJumpInsn Opcodes/GOTO (get mappings $body)) + (.visitLabel $else))) + (doto writer + (.visitInsn Opcodes/POP) + (.visitJumpInsn Opcodes/GOTO $default))) + +(defn ^:private compile-tuple-pm [writer mapping $default ?num-elems ?patterns] + (let [sub-patterns (map (fn [idx] + (map (fn [tup body] + [(nth tup idx) body]) + ?patterns)) + (range ?num-elems)) + subpm-structs (map group-patterns sub-patterns) + [pat-h & pat-t] subpm-structs + (for [(get-branches pat-h) + (cull pat-t)] + ) + (reduce (fn [branches pattern] + ( (group-patterns pattern))) + (get-branches pat-h) + pat-t) + (sequence-tests sub-patterns)] + )) + +(defn ^:private compile-pm [writer mapping pm-struct] + (match pm-struct + [::BoolPM ?patterns ?defaults] + (compile-bool-pm writer mapping $default ?patterns) + + [::IntPM ?patterns ?defaults] + (compile-int-pm writer mapping $default ?patterns) + + [::RealPM ?patterns ?defaults] + (compile-real-pm writer mapping $default ?patterns) + + [::CharPM ?patterns ?defaults] + (compile-char-pm writer mapping $default ?patterns) + + [::TextPM ?patterns ?defaults] + (compile-text-pm writer mapping $default ?patterns) + + [::TuplePM ?num-elems ?patterns ?defaults] + (compile-tuple-pm writer mapping $default ?num-elems ?patterns) + + [::VariantPM ?tags ?patterns ?defaults] + (first ?defaults) + + [::?PM ?defaults] + (first ?defaults) + )) + +(do-template [<name> <pm-tag>] + (defn <name> [pm value body] + (match pm + [<pm-tag> ?branches ?defaults] + (return [<pm-tag> (cons [value body] ?branches) ?defaults]) + + [::?PM ?defaults] + (return [<pm-tag> (list [value body]) ?defaults]) + + _ + (fail "Can't match pattern!"))) + + ^:private group-bool-pm ::BoolPM + ^:private group-int-pm ::IntPM + ^:private group-real-pm ::RealPM + ^:private group-char-pm ::CharPM + ^:private group-text-pm ::textPM + ) + +(defn ^:private group-branch [pm [pattern body]] + (match pattern + [::&parser/Bool ?value] + (group-bool-pm pm ?value body) + + [::&parser/Int ?value] + (group-int-pm pm ?value body) + + [::&parser/Real ?value] + (group-real-pm pm ?value body) + + [::&parser/Char ?value] + (group-char-pm pm ?value body) + + [::&parser/Text ?value] + (group-text-pm pm ?value body) + + [::&parser/Tuple ?members] + (match pm + [::TuplePM ?num-elems ?branches ?defaults] + (exec [_ (assert! (= ?num-elems (count ?members)) + (str "[Analyser Error] Mismatch in tuple size: " ?num-elems " =/= " (count ?members)))] + (return [::TuplePM ?num-elems (cons [?members body] ?branches) ?defaults])) + + [::?PM ?defaults] + (return [::TuplePM (count ?members) (list [?members body]) ?defaults]) + + _ + (fail "Can't match pattern!")) + + [::&parser/Tag ?tag] + (let [members (list) + num-members (count members)] + (match pm + [::VariantPM ?variants ?branches ?defaults] + (exec [variants* (if-let [?num-elems (get ?variants ?tag)] + (exec [_ (assert! (= ?num-elems num-members) + (str "[Analyser Error] Mismatch in tuple size: " ?num-elems " =/= " num-members))] + (return ?variants)) + (return (assoc ?variants ?tag num-members)))] + (return [::VariantPM variants* (conj ?branches [[?tag members] body]) ?defaults])) + + [::?PM ?defaults] + (return [::VariantPM {?tag num-members} (list [[?tag members] body]) ?defaults]) + + _ + (fail "Can't match pattern!"))) + + [::&parser/Form ([[::&parser/Tag ?tag] & ?members] :seq)] + (let [members ?members + num-members (count members)] + (match pm + [::VariantPM ?variants ?branches ?defaults] + (exec [variants* (if-let [?num-elems (get ?variants ?tag)] + (exec [_ (assert! (= ?num-elems num-members) + (str "[Analyser Error] Mismatch in tuple size: " ?num-elems " =/= " num-members))] + (return ?variants)) + (return (assoc ?variants ?tag num-members)))] + (return [::VariantPM variants* (conj ?branches [[?tag members] body]) ?defaults])) + + [::?PM ?defaults] + (return [::VariantPM {?tag num-members} (list [[?tag members] body]) ?defaults]) + + _ + (fail "Can't match pattern!"))) + + [::&parser/Ident ?name] + (match pm + [::BoolPM ?patterns ?defaults] + (return [::BoolPM ?patterns (conj ?defaults [?name body])]) + + [::IntPM ?patterns ?defaults] + (return [::IntPM ?patterns (conj ?defaults [?name body])]) + + [::RealPM ?patterns ?defaults] + (return [::RealPM ?patterns (conj ?defaults [?name body])]) + + [::CharPM ?patterns ?defaults] + (return [::CharPM ?patterns (conj ?defaults [?name body])]) + + [::TextPM ?patterns ?defaults] + (return [::TextPM ?patterns (conj ?defaults [?name body])]) + + [::TuplePM ?num-elems ?patterns ?defaults] + (return [::TuplePM ?num-elems ?patterns (conj ?defaults [?name body])]) + + [::VariantPM ?tags ?patterns ?defaults] + (return [::VariantPM ?tags ?patterns (conj ?defaults [?name body])]) + + [::?PM ?defaults] + (return [::?PM (conj ?defaults [?name body])])) + )) + +(defn ^:private valid-paths [group] + (set (match group + [::BoolPM ?patterns ?defaults] + (concat (map second ?patterns) (map second ?defaults)) + + [::IntPM ?patterns ?defaults] + (concat (map second ?patterns) (map second ?defaults)) + + [::RealPM ?patterns ?defaults] + (concat (map second ?patterns) (map second ?defaults)) + + [::CharPM ?patterns ?defaults] + (concat (map second ?patterns) (map second ?defaults)) + + [::TextPM ?patterns ?defaults] + (concat (map second ?patterns) (map second ?defaults)) + + [::TuplePM ?num-elems ?patterns ?defaults] + (concat (map second ?patterns) (map second ?defaults)) + + [::VariantPM ?tags ?patterns ?defaults] + (concat (map second ?patterns) (map second ?defaults)) + + [::?PM ?defaults] + (map second ?defaults)))) + +(defn ^:private sequence-multi-pm [sequence-pm prev-paths groups] + (match groups + ([head & tail] :seq) + (for [:let [curr-paths (set/intersection prev-paths (valid-paths head))] + [head-paths head-test] (sequence-pm curr-paths head)] + [:multi-test head-test head-paths (sequence-multi-pm sequence-pm head-paths tail)]) + + _ + (list (list)))) + +(do-template [<name> <pm> <test>] + (defn <name> [prev-paths group] + (match group + [<pm> ?patterns ?defaults] + (return (concat (for [[value $body] ?patterns + :when (contains? prev-paths $body)] + [<test> value #{$body}]) + (match ?defaults + ([[default-register $body] & _] :seq) + (list [<test> default-register #{$body}]) + + :else + (list)))) + + :else + (fail ""))) + + ^:private sequence-bool ::BoolPM ::test-bool + ^:private sequence-int ::IntPM ::test-int + ^:private sequence-real ::RealPM ::test-real + ^:private sequence-char ::CharPM ::test-char + ^:private sequence-text ::TextPM ::test-text + ) + +(defn ^:private sequence-? [group] + [::?PM ([[default-register $body] & _] :seq)] + (return (list [<test> default-register #{$body}])) + + :else + (fail "")) + +(defn ^:private sequence-pm [group] + (match group + [::BoolPM _ _] + (sequence-bool group) + + [::IntPM _ _] + (sequence-int group) + + [::RealPM _ _] + (sequence-real group) + + [::CharPM _ _] + (sequence-char group) + + [::TextPM _ _] + (sequence-text group) + + [::?PM _] + (sequence-? group) + + [::TuplePM ?num-elems ?patterns ?defaults] + (exec [:let [sub-patterns (map (fn [idx] + (map (fn [[tup body]] + [(nth tup idx) body]) + ?patterns)) + (range ?num-elems))] + groups (map-m #(reduce-m group-branch [::?PM (list)] %) sub-patterns) + tuple-paths (valid-paths group) + sub-seqs (sequence-multi-pm sequence-pm tuple-paths groups)] + (return (cons [::test-tuple ?num-elems sub-seqs] + (match ?defaults + ([[default-register $body] & _] :seq) + (list [<test> default-register #{$body}]) + + :else + (list))))) + + [::VariantPM ?tags ?patterns ?defaults] + (map-m (fn [tag] + (exec [:let [members+bodies (mapcat (fn [[ptag pmembers pbody]] + (if (= ptag tag) + (list [pmembers pbody]) + (list))) + ?patterns) + sub-patterns (map (fn [idx] + (map (fn [[tup body]] + [(nth tup idx) body]) + members+bodies)) + (range ?num-elems))] + groups (map-m #(reduce-m group-branch [::?PM (list)] %) sub-patterns) + tag-paths (set (map second members+bodies)) + sub-seqs (sequence-multi-pm sequence-pm tag-paths groups)] + (cons [::test-variant tag ?num-elems sub-seqs] + (match ?defaults + ([[default-register $body] & _] :seq) + (list [<test> default-register #{$body}]) + + :else + (list))))) + (keys ?tags)) + )) + +(defn ^:private decision-tree [branches] + (exec [group (reduce-m group-branch [::?PM (list)] branches) + :let [[mappings group*] (map-bodies group) + paths (valid-paths group*)]] + (sequence-pm paths group*))) + +(let [ex-class (->class "java.lang.IllegalStateException")] + (defn ^:private compile-case [compile *type* ?variant ?base-register ?num-registers ?branches] + (exec [*writer* &util/get-writer + :let [$start (new Label) + $end (new Label) + _ (dotimes [offset ?num-registers] + (let [idx (+ ?base-register offset)] + (.visitLocalVariable *writer* (str +local-prefix+ idx) (->java-sig [::&type/Any]) nil $start $end idx)))] + _ (compile ?variant) + :let [_ (doto *writer* + (.visitInsn Opcodes/DUP) + (.visitLabel $start))] + :let [[mapping tree] (decision-tree ?branches)] + + :let [[mappings pm-struct*] (map-bodies pm-struct) + entries (for [[?branch ?body] mappings + :let [label (new Label)]] + [[?branch label] + [label ?body]]) + mappings* (into {} (map first entries)) + ] + :let [$default (new Label) + _ (do (doseq [decision-tree (let [pieces (map first (sequence-parts ?pm-struct))] + (if (get-default pm-struct) + (butlast pieces) + pieces))] + (compile-decision-tree *writer* mappings* $default decision-tree)) + (.visitLabel *writer* $default) + (if-let [[?idx ?body] (get-default pm-struct)] + (doto *writer* + (.visitInsn Opcodes/DUP) + (.visitVarInsn Opcodes/ASTORE ?idx) + (.visitJumpInsn Opcodes/GOTO (get mappings* ?body))) + (doto *writer* + (.visitInsn Opcodes/POP) + (.visitTypeInsn Opcodes/NEW ex-class) + (.visitInsn Opcodes/DUP) + (.visitMethodInsn Opcodes/INVOKESPECIAL ex-class "<init>" "()V") + (.visitInsn Opcodes/ATHROW))))] + _ (map-m (fn [[?label ?body]] + (exec [:let [_ (do (.visitLabel *writer* ?label) + (.visitInsn *writer* Opcodes/POP))] + ret (compile ?body) + :let [_ (.visitJumpInsn *writer* Opcodes/GOTO $end)]] + (return ret))) + (map second entries)) + :let [_ (.visitLabel *writer* $end)]] + (return nil)))) diff --git a/src/lux/compiler/host.clj b/src/lux/compiler/host.clj new file mode 100644 index 000000000..dfe67eece --- /dev/null +++ b/src/lux/compiler/host.clj @@ -0,0 +1,194 @@ + +(let [class+metthod+sig {"boolean" [(->class "java.lang.Boolean") "booleanValue" "()Z"] + "byte" [(->class "java.lang.Byte") "byteValue" "()B"] + "short" [(->class "java.lang.Short") "shortValue" "()S"] + "int" [(->class "java.lang.Integer") "intValue" "()I"] + "long" [(->class "java.lang.Long") "longValue" "()J"] + "float" [(->class "java.lang.Float") "floatValue" "()F"] + "double" [(->class "java.lang.Double") "doubleValue" "()D"] + "char" [(->class "java.lang.Character") "charValue" "()C"]}] + (defn ^:private prepare-arg! [*writer* class-name] + (if-let [[class method sig] (get class+metthod+sig class-name)] + (doto *writer* + (.visitTypeInsn Opcodes/CHECKCAST class) + (.visitMethodInsn Opcodes/INVOKEVIRTUAL class method sig)) + (.visitTypeInsn *writer* Opcodes/CHECKCAST (->class class-name))))) + +;; (let [boolean-class "java.lang.Boolean" +;; integer-class "java.lang.Integer" +;; char-class "java.lang.Character"] +;; (defn prepare-return! [*writer* *type*] +;; (match *type* +;; ::&type/nothing +;; (.visitInsn *writer* Opcodes/ACONST_NULL) + +;; [::&type/primitive "char"] +;; (.visitMethodInsn *writer* Opcodes/INVOKESTATIC (->class char-class) "valueOf" (str "(C)" (->type-signature char-class))) + +;; [::&type/primitive "int"] +;; (.visitMethodInsn *writer* Opcodes/INVOKESTATIC (->class integer-class) "valueOf" (str "(I)" (->type-signature integer-class))) + +;; [::&type/primitive "boolean"] +;; (.visitMethodInsn *writer* Opcodes/INVOKESTATIC (->class boolean-class) "valueOf" (str "(Z)" (->type-signature boolean-class))) + +;; [::&type/Data ?oclass] +;; nil))) + + +(defn ^:private compile-jvm-invokestatic [compile *type* ?class ?method ?classes ?args] + (exec [*writer* &util/get-writer + :let [method-sig (str "(" (reduce str "" (map ->type-signature ?classes)) ")" (->java-sig *type*))] + _ (map-m (fn [[class-name arg]] + (exec [ret (compile arg) + :let [_ (prepare-arg! *writer* class-name)]] + (return ret))) + (map vector ?classes ?args)) + :let [_ (do (.visitMethodInsn *writer* Opcodes/INVOKESTATIC (->class ?class) ?method method-sig) + ;; (prepare-return! *writer* *type*) + )]] + (return nil))) + +(defn ^:private compile-jvm-invokevirtual [compile *type* ?class ?method ?classes ?object ?args] + (exec [*writer* &util/get-writer + :let [method-sig (str "(" (reduce str "" (map ->type-signature ?classes)) ")" (->java-sig *type*))] + _ (compile ?object) + :let [_ (.visitTypeInsn *writer* Opcodes/CHECKCAST (->class ?class))] + _ (map-m (fn [[class-name arg]] + (exec [ret (compile arg) + :let [_ (prepare-arg! *writer* class-name)]] + (return ret))) + (map vector ?classes ?args)) + :let [_ (do (.visitMethodInsn *writer* Opcodes/INVOKEVIRTUAL (->class ?class) ?method method-sig) + ;; (prepare-return! *writer* *type*) + )]] + (return nil))) + +(defn ^:private compile-jvm-new [compile *type* ?class ?classes ?args] + (exec [*writer* &util/get-writer + :let [init-sig (str "(" (reduce str "" (map ->type-signature ?classes)) ")V") + class* (->class ?class) + _ (doto *writer* + (.visitTypeInsn Opcodes/NEW class*) + (.visitInsn Opcodes/DUP))] + _ (map-m (fn [[class-name arg]] + (exec [ret (compile arg) + :let [_ (prepare-arg! *writer* class-name)]] + (return ret))) + (map vector ?classes ?args)) + :let [_ (doto *writer* + (.visitMethodInsn Opcodes/INVOKESPECIAL class* "<init>" init-sig))]] + (return nil))) + +(defn ^:private compile-jvm-new-array [compile *type* ?class ?length] + (exec [*writer* &util/get-writer + :let [_ (doto *writer* + (.visitLdcInsn (int ?length)) + (.visitTypeInsn Opcodes/ANEWARRAY (->class ?class)))]] + (return nil))) + +(defn ^:private compile-jvm-aastore [compile *type* ?array ?idx ?elem] + (exec [*writer* &util/get-writer + _ (compile ?array) + :let [_ (doto *writer* + (.visitInsn Opcodes/DUP) + (.visitLdcInsn (int ?idx)))] + _ (compile ?elem) + :let [_ (.visitInsn *writer* Opcodes/AASTORE)]] + (return nil))) + +(defn ^:private compile-jvm-aaload [compile *type* ?array ?idx] + (exec [*writer* &util/get-writer + _ (compile ?array) + :let [_ (doto *writer* + (.visitLdcInsn (int ?idx)) + (.visitInsn Opcodes/AALOAD))]] + (return nil))) + +(defn ^:private compile-jvm-getstatic [compile *type* ?class ?field] + (exec [*writer* &util/get-writer + :let [_ (.visitFieldInsn *writer* Opcodes/GETSTATIC (->class ?class) ?field (->java-sig *type*))]] + (return nil))) + +(defn ^:private compile-jvm-getfield [compile *type* ?class ?field ?object] + (exec [*writer* &util/get-writer + _ (compile ?object) + :let [_ (.visitTypeInsn *writer* Opcodes/CHECKCAST (->class ?class))] + :let [_ (.visitFieldInsn *writer* Opcodes/GETFIELD (->class ?class) ?field (->java-sig *type*))]] + (return nil))) + +(defn ^:private compile-jvm-class [compile *type* ?package ?name ?super-class ?fields ?methods] + (let [parent-dir (->package ?package) + full-name (str parent-dir "/" ?name) + super-class* (->class ?super-class) + =class (doto (new ClassWriter ClassWriter/COMPUTE_MAXS) + (.visit Opcodes/V1_5 (+ Opcodes/ACC_PUBLIC Opcodes/ACC_SUPER) + full-name nil super-class* nil)) + _ (do (doseq [[field props] ?fields] + (doto (.visitField =class Opcodes/ACC_PUBLIC field (->type-signature (:type props)) nil nil) + (.visitEnd))) + (doto (.visitMethod =class Opcodes/ACC_PUBLIC "<init>" "()V" nil nil) + (.visitCode) + (.visitVarInsn Opcodes/ALOAD 0) + (.visitMethodInsn Opcodes/INVOKESPECIAL super-class* "<init>" "()V") + (.visitInsn Opcodes/RETURN) + (.visitMaxs 0 0) + (.visitEnd)) + (.visitEnd =class) + (.mkdirs (java.io.File. (str "output/" parent-dir))))] + (save-class! full-name (.toByteArray =class)))) + +(defn ^:private compile-jvm-interface [compile *type* ?package ?name ?fields ?methods] + (let [parent-dir (->package ?package) + full-name (str parent-dir "/" ?name) + =interface (doto (new ClassWriter ClassWriter/COMPUTE_MAXS) + (.visit Opcodes/V1_5 (+ Opcodes/ACC_PUBLIC Opcodes/ACC_INTERFACE) + full-name nil "java/lang/Object" nil)) + _ (do (doseq [[?method ?props] ?methods + :let [[?args ?return] (:type ?props) + signature (str "(" (reduce str "" (map ->type-signature ?args)) ")" (->type-signature ?return))]] + (.visitMethod =interface (+ Opcodes/ACC_PUBLIC Opcodes/ACC_ABSTRACT) ?method signature nil nil)) + (.visitEnd =interface) + (.mkdirs (java.io.File. (str "output/" parent-dir))))] + (save-class! full-name (.toByteArray =interface)))) + +(do-template [<name> <opcode> <wrapper-class> <value-method> <value-method-sig> <wrapper-method> <wrapper-method-sig>] + (defn <name> [compile *type* ?x ?y] + (exec [:let [+wrapper-class+ (->class <wrapper-class>)] + *writer* &util/get-writer + _ (compile ?x) + :let [_ (doto *writer* + (.visitTypeInsn Opcodes/CHECKCAST +wrapper-class+) + (.visitMethodInsn Opcodes/INVOKEVIRTUAL +wrapper-class+ <value-method> <value-method-sig>))] + _ (compile ?y) + :let [_ (doto *writer* + (.visitTypeInsn Opcodes/CHECKCAST +wrapper-class+) + (.visitMethodInsn Opcodes/INVOKEVIRTUAL +wrapper-class+ <value-method> <value-method-sig>)) + _ (doto *writer* + (.visitInsn <opcode>) + (.visitMethodInsn Opcodes/INVOKESTATIC +wrapper-class+ <wrapper-method> (str <wrapper-method-sig> (->type-signature <wrapper-class>))))]] + (return nil))) + + ^:private compile-jvm-iadd Opcodes/IADD "java.lang.Integer" "intValue" "()I" "valueOf" "(I)" + ^:private compile-jvm-isub Opcodes/ISUB "java.lang.Integer" "intValue" "()I" "valueOf" "(I)" + ^:private compile-jvm-imul Opcodes/IMUL "java.lang.Integer" "intValue" "()I" "valueOf" "(I)" + ^:private compile-jvm-idiv Opcodes/IDIV "java.lang.Integer" "intValue" "()I" "valueOf" "(I)" + ^:private compile-jvm-irem Opcodes/IREM "java.lang.Integer" "intValue" "()I" "valueOf" "(I)" + + ^:private compile-jvm-ladd Opcodes/LADD "java.lang.Long" "longValue" "()J" "valueOf" "(J)" + ^:private compile-jvm-lsub Opcodes/LSUB "java.lang.Long" "longValue" "()J" "valueOf" "(J)" + ^:private compile-jvm-lmul Opcodes/LMUL "java.lang.Long" "longValue" "()J" "valueOf" "(J)" + ^:private compile-jvm-ldiv Opcodes/LDIV "java.lang.Long" "longValue" "()J" "valueOf" "(J)" + ^:private compile-jvm-lrem Opcodes/LREM "java.lang.Long" "longValue" "()J" "valueOf" "(J)" + + ^:private compile-jvm-fadd Opcodes/FADD "java.lang.Float" "floatValue" "()F" "valueOf" "(F)" + ^:private compile-jvm-fsub Opcodes/FSUB "java.lang.Float" "floatValue" "()F" "valueOf" "(F)" + ^:private compile-jvm-fmul Opcodes/FMUL "java.lang.Float" "floatValue" "()F" "valueOf" "(F)" + ^:private compile-jvm-fdiv Opcodes/FDIV "java.lang.Float" "floatValue" "()F" "valueOf" "(F)" + ^:private compile-jvm-frem Opcodes/FREM "java.lang.Float" "floatValue" "()F" "valueOf" "(F)" + + ^:private compile-jvm-dadd Opcodes/DADD "java.lang.Double" "doubleValue" "()D" "valueOf" "(D)" + ^:private compile-jvm-dsub Opcodes/DSUB "java.lang.Double" "doubleValue" "()D" "valueOf" "(D)" + ^:private compile-jvm-dmul Opcodes/DMUL "java.lang.Double" "doubleValue" "()D" "valueOf" "(D)" + ^:private compile-jvm-ddiv Opcodes/DDIV "java.lang.Double" "doubleValue" "()D" "valueOf" "(D)" + ^:private compile-jvm-drem Opcodes/DREM "java.lang.Double" "doubleValue" "()D" "valueOf" "(D)" + ) diff --git a/src/lux/compiler/lambda.clj b/src/lux/compiler/lambda.clj new file mode 100644 index 000000000..b3bfc4860 --- /dev/null +++ b/src/lux/compiler/lambda.clj @@ -0,0 +1,176 @@ +(ns lux.compiler.lambda) + +;; [Utils] +(def ^:private clo-field-sig (->type-signature "java.lang.Object")) +(def ^:private lambda-return-sig (->type-signature "java.lang.Object")) +(def ^:private <init>-return "V") +(def ^:private counter-sig "I") +(def ^:private +datum-sig+ (->type-signature "java.lang.Object")) + +(defn ^:private lambda-impl-signature [args] + (str (reduce str "(" (repeat (count args) clo-field-sig)) ")" lambda-return-sig)) + +(defn ^:private lambda-<init>-signature [closed-over args] + (let [num-args (count args)] + (str "(" (reduce str "" (repeat (count closed-over) clo-field-sig)) + (if (> num-args 1) + (reduce str counter-sig (repeat (dec num-args) clo-field-sig))) + ")" + <init>-return))) + +(defn ^:private add-lambda-<init> [class class-name closed-over args init-signature] + (let [num-args (count args) + num-mappings (count closed-over)] + (doto (.visitMethod class Opcodes/ACC_PUBLIC "<init>" init-signature nil nil) + (.visitCode) + (.visitVarInsn Opcodes/ALOAD 0) + (.visitMethodInsn Opcodes/INVOKESPECIAL "java/lang/Object" "<init>" "()V") + (-> (doto (.visitVarInsn Opcodes/ALOAD 0) + (.visitVarInsn Opcodes/ALOAD ?captured-id) + (.visitFieldInsn Opcodes/PUTFIELD class-name captured-name clo-field-sig)) + (->> (let [captured-name (str +closure-prefix+ ?captured-id)]) + (match (:form ?captured) + [::&analyser/captured ?closure-id ?captured-id ?source]) + (doseq [[?name ?captured] closed-over]))) + (-> (doto (.visitVarInsn Opcodes/ALOAD 0) + (.visitInsn Opcodes/ICONST_0) + (.visitFieldInsn Opcodes/PUTFIELD class-name "_counter" counter-sig) + (-> (doto (.visitVarInsn Opcodes/ALOAD 0) + (.visitVarInsn Opcodes/ALOAD (+ clo_idx offset)) + (.visitFieldInsn Opcodes/PUTFIELD class-name field-name clo-field-sig)) + (->> (let [field-name (str +partial-prefix+ clo_idx)] + (doto (.visitField class (+ Opcodes/ACC_PRIVATE Opcodes/ACC_FINAL) field-name clo-field-sig nil nil) + (.visitEnd))) + (dotimes [clo_idx (dec num-args)]) + (let [offset (+ 2 num-mappings)])))) + (->> (when (> num-args 1)))) + (.visitInsn Opcodes/RETURN) + (.visitMaxs 0 0) + (.visitEnd)))) + +(do-template [<name> <prefix>] + (defn <name> [writer class-name vars] + (dotimes [idx (count vars)] + (doto writer + (.visitVarInsn Opcodes/ALOAD 0) + (.visitFieldInsn Opcodes/GETFIELD class-name (str <prefix> idx) clo-field-sig)))) + + ^:private add-closure-vars +closure-prefix+ + ^:private add-partial-vars +partial-prefix+ + ) + +(defn ^:private add-nulls [writer amount] + (dotimes [_ amount] + (.visitInsn writer Opcodes/ACONST_NULL))) + +(defn ^:private add-lambda-apply [class class-name closed-over args impl-signature init-signature] + (let [num-args (count args) + num-captured (dec num-args) + default-label (new Label) + branch-labels (for [_ (range num-captured)] + (new Label))] + (doto (.visitMethod class Opcodes/ACC_PUBLIC "apply" +apply-signature+ nil nil) + (.visitCode) + (-> (doto (.visitVarInsn Opcodes/ALOAD 0) + (.visitFieldInsn Opcodes/GETFIELD class-name "_counter" counter-sig) + (.visitTableSwitchInsn 0 (dec num-captured) default-label (into-array Label branch-labels)) + (-> (doto (.visitLabel branch-label) + (.visitTypeInsn Opcodes/NEW class-name) + (.visitInsn Opcodes/DUP) + (add-closure-vars class-name closed-over) + (.visitLdcInsn (int current-captured)) + (add-partial-vars class-name (take current-captured args)) + (.visitVarInsn Opcodes/ALOAD 1) + (add-nulls (- (dec num-captured) current-captured)) + (.visitMethodInsn Opcodes/INVOKESPECIAL class-name "<init>" init-signature) + (.visitInsn Opcodes/ARETURN)) + (->> (doseq [[branch-label current-captured] (map vector branch-labels (range (count branch-labels)))]))) + (.visitLabel default-label)) + (->> (when (> num-args 1)))) + (.visitVarInsn Opcodes/ALOAD 0) + (add-partial-vars class-name (butlast args)) + (.visitVarInsn Opcodes/ALOAD 1) + (.visitMethodInsn Opcodes/INVOKEVIRTUAL class-name "impl" impl-signature) + (.visitInsn Opcodes/ARETURN) + (.visitMaxs 0 0) + (.visitEnd)))) + +(defn ^:private add-lambda-impl [class compile impl-signature impl-body] + (&util/with-writer (doto (.visitMethod class Opcodes/ACC_PUBLIC "impl" impl-signature nil nil) + (.visitCode)) + (exec [;; :let [_ (prn 'add-lambda-impl/_0)] + *writer* &util/get-writer + ;; :let [_ (prn 'add-lambda-impl/_1 *writer*)] + ret (compile impl-body) + ;; :let [_ (prn 'add-lambda-impl/_2 ret)] + :let [_ (doto *writer* + (.visitInsn Opcodes/ARETURN) + (.visitMaxs 0 0) + (.visitEnd))] + ;; :let [_ (prn 'add-lambda-impl/_3)] + ] + (return ret)))) + +(defn ^:private instance-closure [compile lambda-class closed-over args init-signature] + (exec [*writer* &util/get-writer + :let [_ (doto *writer* + (.visitTypeInsn Opcodes/NEW lambda-class) + (.visitInsn Opcodes/DUP))] + _ (->> closed-over + (sort #(< (-> %1 second :form (nth 2)) + (-> %2 second :form (nth 2)))) + (map-m (fn [[?name ?captured]] + (match (:form ?captured) + [::&analyser/captured ?closure-id ?captured-id ?source] + (compile ?source))))) + :let [num-args (count args) + _ (do (when (> num-args 1) + (.visitInsn *writer* Opcodes/ICONST_0) + (add-nulls *writer* (dec num-args))) + (.visitMethodInsn *writer* Opcodes/INVOKESPECIAL lambda-class "<init>" init-signature))]] + (return nil))) + +(defn ^:private add-lambda-<clinit> [class class-name args <init>-sig] + (let [num-args (count args)] + (doto (.visitMethod class Opcodes/ACC_PUBLIC "<clinit>" "()V" nil nil) + (.visitCode) + (.visitTypeInsn Opcodes/NEW class-name) + (.visitInsn Opcodes/DUP) + (-> (doto (.visitInsn *writer* Opcodes/ICONST_0) + (add-nulls (dec num-args))) + (->> (when (> num-args 1)))) + (.visitMethodInsn Opcodes/INVOKESPECIAL class-name "<init>" <init>-sig) + (.visitFieldInsn Opcodes/PUTSTATIC class-name "_datum" +datum-sig+) + (.visitInsn Opcodes/RETURN) + (.visitMaxs 0 0) + (.visitEnd)))) + +;; [Resources] +(defn compile-lambda [compile *type* ?scope ?closure ?args ?body with-datum? instance?] + (exec [:let [lambda-class (&host/location ?scope) + impl-signature (lambda-impl-signature ?args) + <init>-sig (lambda-<init>-signature ?closure ?args) + =class (doto (new ClassWriter ClassWriter/COMPUTE_MAXS) + (.visit Opcodes/V1_5 (+ Opcodes/ACC_PUBLIC Opcodes/ACC_FINAL Opcodes/ACC_SUPER) + lambda-class nil "java/lang/Object" (into-array [(->class +function-class+)])) + (-> (doto (.visitField (+ Opcodes/ACC_PRIVATE Opcodes/ACC_FINAL) captured-name clo-field-sig nil nil) + (.visitEnd)) + (->> (let [captured-name (str +closure-prefix+ ?captured-id)]) + (match (:form ?captured) + [::&analyser/captured ?closure-id ?captured-id ?source]) + (doseq [[?name ?captured] ?closure]))) + (-> (doto (.visitField (+ Opcodes/ACC_PRIVATE Opcodes/ACC_FINAL) "_counter" counter-sig nil nil) + (.visitEnd)) + (->> (when (> (count ?args) 1)))) + (-> (doto (.visitField (+ Opcodes/ACC_PUBLIC Opcodes/ACC_STATIC) "_datum" +datum-sig+ nil nil) + (add-lambda-<clinit> lambda-class ?args <init>-sig)) + (when with-datum?)) + (add-lambda-apply lambda-class ?closure ?args impl-signature <init>-sig) + (add-lambda-<init> lambda-class ?closure ?args <init>-sig) + )] + _ (add-lambda-impl =class compile impl-signature ?body) + :let [_ (.visitEnd =class)] + _ (save-class! lambda-class (.toByteArray =class))] + (if instance? + (instance-closure compile lambda-class ?closure ?args <init>-sig) + (return nil)))) |