diff options
-rw-r--r-- | README.md | 23 | ||||
-rw-r--r-- | src/lux/analyser.clj | 4 | ||||
-rw-r--r-- | src/lux/analyser/base.clj | 1 | ||||
-rw-r--r-- | src/lux/analyser/case.clj | 23 | ||||
-rw-r--r-- | src/lux/analyser/host.clj | 140 | ||||
-rw-r--r-- | src/lux/analyser/lux.clj | 4 | ||||
-rw-r--r-- | src/lux/analyser/module.clj | 55 | ||||
-rw-r--r-- | src/lux/base.clj | 7 | ||||
-rw-r--r-- | src/lux/compiler.clj | 9 | ||||
-rw-r--r-- | src/lux/compiler/case.clj | 10 | ||||
-rw-r--r-- | src/lux/compiler/host.clj | 311 | ||||
-rw-r--r-- | src/lux/compiler/lux.clj | 31 | ||||
-rw-r--r-- | src/lux/compiler/type.clj | 4 | ||||
-rw-r--r-- | src/lux/host.clj | 59 | ||||
-rw-r--r-- | src/lux/lexer.clj | 14 | ||||
-rw-r--r-- | src/lux/optimizer.clj | 237 | ||||
-rw-r--r-- | src/lux/parser.clj | 3 | ||||
-rw-r--r-- | src/lux/type.clj | 38 | ||||
-rw-r--r-- | src/lux/type/host.clj | 11 |
19 files changed, 837 insertions, 147 deletions
@@ -7,20 +7,14 @@ It's meant to be a functional, statically-typed Lisp that will run on several pl ### What's the current version? -0.3.3 +0.4.0 ### How far ahead is the project? -The Java-bytecode compiler is feature-complete. - -Optimizations and other minor improvements are on the way. +Lux is finally in the **beta** stage. The JVM compiler is pretty stable and the standard library has grown to a respectable size. ### How can I use it? -Download the 0.3.3 compiler from here: https://github.com/LuxLang/lux/releases/download/0.3.3/luxc.jar - -Once you download the compiler, you'll want to create a directory named "source" in the same directory where the compiler is located. - You should use the Leiningen plugin for Lux to compile your programs and manager your dependencies. You can find it here: https://github.com/LuxLang/lux-lein @@ -35,8 +29,6 @@ To take a look at a sample Lux project, please take a look at this repository: h The program in there was actually used to generate most of the documentation for the standard library in the wiki (located over here: https://github.com/LuxLang/lux/wiki/Standard-Library) -You can also checkout this tutorial, which includes a sample TODO web-app: http://luxlang.blogspot.com/2015/12/lux-tutorial-1-simple-todo-list-using.html - ### What's the license? Mozilla Public License v2.0 @@ -87,8 +79,7 @@ Functions are curried and partial application is as simple as just applying a fu e.g. - (let [inc (i+ 1)] - (map inc (list 1 2 3 4 5))) + (map (+ 1) (list 1 2 3 4 5)) ### Code portability @@ -122,21 +113,21 @@ But you can also use them to destructure them inside pattern-matching: (case (: (List Int) (list 1 2 3)) (#Cons x (#Cons y (#Cons z #Nil))) - (#Some ($ int:* x y z)) + (#Some ($_ * x y z)) _ #None) (case (: (List Int) (list 1 2 3)) (\ (list x y z)) - (#Some ($ int:* x y z)) + (#Some ($_ * x y z)) _ #None) There is also the special **\or** macro, which introduces *or patterns*: - (deftype Weekday + (type: Weekday (| #Monday #Tuesday #Wednesday @@ -145,7 +136,7 @@ There is also the special **\or** macro, which introduces *or patterns*: #Saturday #Sunday)) - (def (weekend? day) + (def: (weekend? day) (-> Weekday Bool) (case day (\or #Saturday #Sunday) diff --git a/src/lux/analyser.clj b/src/lux/analyser.clj index d6cc5cfda..2ad3745d8 100644 --- a/src/lux/analyser.clj +++ b/src/lux/analyser.clj @@ -71,6 +71,10 @@ (|do [_ (&type/check exo-type &type/Bool)] (return (&/|list (&&/|meta exo-type cursor (&&/$bool ?value))))) + (&/$NatS ?value) + (|do [_ (&type/check exo-type &type/Nat)] + (return (&/|list (&&/|meta exo-type cursor (&&/$nat ?value))))) + (&/$IntS ?value) (|do [_ (&type/check exo-type &type/Int)] (return (&/|list (&&/|meta exo-type cursor (&&/$int ?value))))) diff --git a/src/lux/analyser/base.clj b/src/lux/analyser/base.clj index fed65bb29..45d111249 100644 --- a/src/lux/analyser/base.clj +++ b/src/lux/analyser/base.clj @@ -12,6 +12,7 @@ ;; [Tags] (defvariant ("bool" 1) + ("nat" 1) ("int" 1) ("real" 1) ("char" 1) diff --git a/src/lux/analyser/case.clj b/src/lux/analyser/case.clj index 8b1ee3a89..bccbd4a07 100644 --- a/src/lux/analyser/case.clj +++ b/src/lux/analyser/case.clj @@ -18,6 +18,7 @@ (defvariant ("DefaultTotal" 1) ("BoolTotal" 2) + ("NatTotal" 2) ("IntTotal" 2) ("RealTotal" 2) ("CharTotal" 2) @@ -29,6 +30,7 @@ ("NoTestAC" 0) ("StoreTestAC" 1) ("BoolTestAC" 1) + ("NatTestAC" 1) ("IntTestAC" 1) ("RealTestAC" 1) ("CharTestAC" 1) @@ -265,6 +267,11 @@ =kont kont] (return (&/T [($BoolTestAC ?value) =kont]))) + (&/$NatS ?value) + (|do [_ (&type/check value-type &type/Nat) + =kont kont] + (return (&/T [($NatTestAC ?value) =kont]))) + (&/$IntS ?value) (|do [_ (&type/check value-type &type/Int) =kont kont] @@ -394,6 +401,9 @@ [($BoolTotal total? ?values) ($NoTestAC)] (return ($BoolTotal true ?values)) + [($NatTotal total? ?values) ($NoTestAC)] + (return ($NatTotal true ?values)) + [($IntTotal total? ?values) ($NoTestAC)] (return ($IntTotal true ?values)) @@ -418,6 +428,9 @@ [($BoolTotal total? ?values) ($StoreTestAC ?idx)] (return ($BoolTotal true ?values)) + [($NatTotal total? ?values) ($StoreTestAC ?idx)] + (return ($NatTotal true ?values)) + [($IntTotal total? ?values) ($StoreTestAC ?idx)] (return ($IntTotal true ?values)) @@ -442,6 +455,12 @@ [($BoolTotal total? ?values) ($BoolTestAC ?value)] (return ($BoolTotal total? (&/$Cons ?value ?values))) + [($DefaultTotal total?) ($NatTestAC ?value)] + (return ($NatTotal total? (&/|list ?value))) + + [($NatTotal total? ?values) ($NatTestAC ?value)] + (return ($NatTotal total? (&/$Cons ?value ?values))) + [($DefaultTotal total?) ($IntTestAC ?value)] (return ($IntTotal total? (&/|list ?value))) @@ -527,6 +546,10 @@ (return (or ?total (= #{true false} (set (&/->seq ?values)))))) + ($NatTotal ?total _) + (|do [_ (&type/check value-type &type/Nat)] + (return ?total)) + ($IntTotal ?total _) (|do [_ (&type/check value-type &type/Int)] (return ?total)) diff --git a/src/lux/analyser/host.clj b/src/lux/analyser/host.clj index be69dc54c..19971d95a 100644 --- a/src/lux/analyser/host.clj +++ b/src/lux/analyser/host.clj @@ -34,7 +34,8 @@ (&/|map #(Class/forName % true class-loader)))] (if-let [missing-ex (&/fold (fn [prev ^Class now] (or prev - (cond (.isAssignableFrom java.lang.RuntimeException now) + (cond (or (.isAssignableFrom java.lang.RuntimeException now) + (.isAssignableFrom java.lang.Error now)) nil (&/fold (fn [found? ^Class ex-catch] @@ -464,11 +465,17 @@ ^:private analyse-jvm-l2d "l2d" "java.lang.Long" "java.lang.Double" ^:private analyse-jvm-l2f "l2f" "java.lang.Long" "java.lang.Float" ^:private analyse-jvm-l2i "l2i" "java.lang.Long" "java.lang.Integer" + ^:private analyse-jvm-l2s "l2i" "java.lang.Long" "java.lang.Short" + ^:private analyse-jvm-l2b "l2i" "java.lang.Long" "java.lang.Byte" ^:private analyse-jvm-c2b "c2b" "java.lang.Character" "java.lang.Byte" ^:private analyse-jvm-c2s "c2s" "java.lang.Character" "java.lang.Short" ^:private analyse-jvm-c2i "c2i" "java.lang.Character" "java.lang.Integer" ^:private analyse-jvm-c2l "c2l" "java.lang.Character" "java.lang.Long" + + ^:private analyse-jvm-s2l "s2l" "java.lang.Short" "java.lang.Long" + + ^:private analyse-jvm-b2l "b2l" "java.lang.Byte" "java.lang.Long" ) (do-template [<name> <proc> <v1-class> <v2-class> <to-class>] @@ -549,8 +556,8 @@ ^:private analyse-jvm-dgt "dgt" "java.lang.Double" "java.lang.Boolean" ) -(let [length-type &type/Int - idx-type &type/Int] +(let [length-type &type/Nat + idx-type &type/Nat] (do-template [<elem-class> <array-class> <new-name> <new-tag> <load-name> <load-tag> <store-name> <store-tag>] (let [elem-type (&/$HostT <elem-class> &/$Nil) array-type (&/$HostT <array-class> &/$Nil)] @@ -599,8 +606,8 @@ ;; else false))) -(let [length-type &type/Int - idx-type &type/Int] +(let [length-type &type/Nat + idx-type &type/Nat] (defn ^:private analyse-jvm-anewarray [analyse exo-type ?values] (|do [:let [(&/$Cons [_ (&/$TextS _gclass)] (&/$Cons length (&/$Nil))) ?values] gclass (&reader/with-source "jvm-anewarray" _gclass @@ -645,7 +652,7 @@ =array (&&/analyse-1+ analyse array) [arr-class arr-params] (ensure-object (&&/expr-type* =array)) _ (&/assert! (array-class? arr-class) (str "[Analyser Error] Expected array. Instead got: " arr-class)) - _ (&type/check exo-type &type/Int) + _ (&type/check exo-type &type/Nat) _cursor &/cursor] (return (&/|list (&&/|meta exo-type _cursor (&&/$proc (&/T ["jvm" "arraylength"]) (&/|list =array) (&/|list)) @@ -885,8 +892,8 @@ (return (&/|list (&&/|meta output-type _cursor (&&/$proc (&/T ["jvm" "load-class"]) (&/|list) (&/|list _class-name output-type))))))) -(let [length-type &type/Int - idx-type &type/Int] +(let [length-type &type/Nat + idx-type &type/Nat] (defn ^:private analyse-array-new [analyse exo-type ?values] (|do [:let [(&/$Cons length (&/$Nil)) ?values] :let [gclass (&/$GenericClass "java.lang.Object" (&/|list)) @@ -1008,9 +1015,9 @@ (do-template [<name> <op>] (defn <name> [analyse exo-type ?values] (|do [:let [(&/$Cons input (&/$Cons mask (&/$Nil))) ?values] - =mask (&&/analyse-1 analyse &type/Int mask) - =input (&&/analyse-1 analyse &type/Int input) - _ (&type/check exo-type &type/Int) + =mask (&&/analyse-1 analyse &type/Nat mask) + =input (&&/analyse-1 analyse &type/Nat input) + _ (&type/check exo-type &type/Nat) _cursor &/cursor] (return (&/|list (&&/|meta exo-type _cursor (&&/$proc (&/T ["bit" <op>]) (&/|list =input =mask) (&/|list))))))) @@ -1022,25 +1029,25 @@ (defn ^:private analyse-bit-count [analyse exo-type ?values] (|do [:let [(&/$Cons input (&/$Nil)) ?values] - =input (&&/analyse-1 analyse &type/Int input) - _ (&type/check exo-type &type/Int) + =input (&&/analyse-1 analyse &type/Nat input) + _ (&type/check exo-type &type/Nat) _cursor &/cursor] (return (&/|list (&&/|meta exo-type _cursor (&&/$proc (&/T ["bit" "count"]) (&/|list =input) (&/|list))))))) -(do-template [<name> <op>] +(do-template [<name> <op> <type>] (defn <name> [analyse exo-type ?values] (|do [:let [(&/$Cons input (&/$Cons shift (&/$Nil))) ?values] - =shift (&&/analyse-1 analyse &type/Int shift) - =input (&&/analyse-1 analyse &type/Int input) - _ (&type/check exo-type &type/Int) + =shift (&&/analyse-1 analyse &type/Nat shift) + =input (&&/analyse-1 analyse <type> input) + _ (&type/check exo-type <type>) _cursor &/cursor] (return (&/|list (&&/|meta exo-type _cursor (&&/$proc (&/T ["bit" <op>]) (&/|list =input =shift) (&/|list))))))) - ^:private analyse-bit-shift-left "shift-left" - ^:private analyse-bit-shift-right "shift-right" - ^:private analyse-bit-unsigned-shift-right "unsigned-shift-right" + ^:private analyse-bit-shift-left "shift-left" &type/Nat + ^:private analyse-bit-shift-right "shift-right" &type/Int + ^:private analyse-bit-unsigned-shift-right "unsigned-shift-right" &type/Nat ) (defn ^:private analyse-lux-== [analyse exo-type ?values] @@ -1054,6 +1061,68 @@ (return (&/|list (&&/|meta exo-type _cursor (&&/$proc (&/T ["lux" "=="]) (&/|list =left =right) (&/|list))))))))) +(do-template [<name> <proc> <input-type> <output-type>] + (defn <name> [analyse exo-type ?values] + (|do [:let [(&/$Cons x (&/$Cons y (&/$Nil))) ?values] + =x (&&/analyse-1 analyse <input-type> x) + =y (&&/analyse-1 analyse <input-type> y) + _ (&type/check exo-type <output-type>) + _cursor &/cursor] + (return (&/|list (&&/|meta <output-type> _cursor + (&&/$proc (&/T ["nat" <proc>]) (&/|list =x =y) (&/|list))))))) + + ^:private analyse-nat-add "add" &type/Nat &type/Nat + ^:private analyse-nat-sub "sub" &type/Nat &type/Nat + ^:private analyse-nat-mul "mul" &type/Nat &type/Nat + ^:private analyse-nat-div "div" &type/Nat &type/Nat + ^:private analyse-nat-rem "rem" &type/Nat &type/Nat + ^:private analyse-nat-eq "eq" &type/Nat &type/Bool + ^:private analyse-nat-lt "lt" &type/Nat &type/Bool + ) + +(defn ^:private analyse-nat-encode [analyse exo-type ?values] + (|do [:let [(&/$Cons x (&/$Nil)) ?values] + =x (&&/analyse-1 analyse &type/Nat x) + _ (&type/check exo-type &type/Text) + _cursor &/cursor] + (return (&/|list (&&/|meta &type/Text _cursor + (&&/$proc (&/T ["nat" "encode"]) (&/|list =x) (&/|list))))))) + +(defn ^:private analyse-nat-decode [analyse exo-type ?values] + (|do [:let [(&/$Cons x (&/$Nil)) ?values] + =x (&&/analyse-1 analyse &type/Text x) + _ (&type/check exo-type (&/$AppT &type/Maybe &type/Nat)) + _cursor &/cursor] + (return (&/|list (&&/|meta (&/$AppT &type/Maybe &type/Nat) _cursor + (&&/$proc (&/T ["nat" "decode"]) (&/|list =x) (&/|list))))))) + +(do-template [<name> <type> <op>] + (defn <name> [analyse exo-type ?values] + (|do [:let [(&/$Nil) ?values] + _ (&type/check exo-type <type>) + _cursor &/cursor] + (return (&/|list (&&/|meta <type> _cursor + (&&/$proc (&/T <op>) (&/|list) (&/|list))))))) + + ^:private analyse-nat-min-value &type/Nat ["nat" "min-value"] + ^:private analyse-nat-max-value &type/Nat ["nat" "max-value"] + ) + +(do-template [<name> <from-type> <to-type> <op>] + (defn <name> [analyse exo-type ?values] + (|do [:let [(&/$Cons x (&/$Nil)) ?values] + =x (&&/analyse-1 analyse <from-type> x) + _ (&type/check exo-type <to-type>) + _cursor &/cursor] + (return (&/|list (&&/|meta <to-type> _cursor + (&&/$proc (&/T <op>) (&/|list =x) (&/|list))))))) + + ^:private analyse-nat-to-int &type/Nat &type/Int ["nat" "to-int"] + ^:private analyse-nat-to-char &type/Nat &type/Char ["nat" "to-char"] + ^:private analyse-int-to-nat &type/Int &type/Nat ["int" "to-nat"] + ^:private analyse-char-to-nat &type/Char &type/Nat ["char" "to-nat"] + ) + (defn analyse-host [analyse exo-type compilers category proc ?values] (|let [[_ _ compile-class compile-interface] compilers] (case category @@ -1078,6 +1147,33 @@ "put" (analyse-jvm-aastore analyse exo-type ?values) "remove" (analyse-array-remove analyse exo-type ?values) "size" (analyse-jvm-arraylength analyse exo-type ?values)) + + "nat" + (case proc + "+" (analyse-nat-add analyse exo-type ?values) + "-" (analyse-nat-sub analyse exo-type ?values) + "*" (analyse-nat-mul analyse exo-type ?values) + "/" (analyse-nat-div analyse exo-type ?values) + "%" (analyse-nat-rem analyse exo-type ?values) + "=" (analyse-nat-eq analyse exo-type ?values) + "<" (analyse-nat-lt analyse exo-type ?values) + "encode" (analyse-nat-encode analyse exo-type ?values) + "decode" (analyse-nat-decode analyse exo-type ?values) + "min-value" (analyse-nat-min-value analyse exo-type ?values) + "max-value" (analyse-nat-max-value analyse exo-type ?values) + "to-int" (analyse-nat-to-int analyse exo-type ?values) + "to-char" (analyse-nat-to-char analyse exo-type ?values) + ) + + "int" + (case proc + "to-nat" (analyse-int-to-nat analyse exo-type ?values) + ) + + "char" + (case proc + "to-nat" (analyse-char-to-nat analyse exo-type ?values) + ) "jvm" (case proc @@ -1162,10 +1258,14 @@ "l2d" (analyse-jvm-l2d analyse exo-type ?values) "l2f" (analyse-jvm-l2f analyse exo-type ?values) "l2i" (analyse-jvm-l2i analyse exo-type ?values) + "l2s" (analyse-jvm-l2s analyse exo-type ?values) + "l2b" (analyse-jvm-l2b analyse exo-type ?values) "c2b" (analyse-jvm-c2b analyse exo-type ?values) "c2s" (analyse-jvm-c2s analyse exo-type ?values) "c2i" (analyse-jvm-c2i analyse exo-type ?values) "c2l" (analyse-jvm-c2l analyse exo-type ?values) + "b2l" (analyse-jvm-b2l analyse exo-type ?values) + "s2l" (analyse-jvm-s2l analyse exo-type ?values) ;; else (->> (&/fail-with-loc (str "[Analyser Error] Unknown host procedure: " [category proc])) (if-let [[_ _def-code] (re-find #"^interface:(.*)$" proc)] diff --git a/src/lux/analyser/lux.clj b/src/lux/analyser/lux.clj index c50f26437..b4f87f140 100644 --- a/src/lux/analyser/lux.clj +++ b/src/lux/analyser/lux.clj @@ -268,14 +268,14 @@ (defn ^:private analyse-global [analyse exo-type module name] (|do [[[r-module r-name] [endo-type ?meta ?value]] (&&module/find-def module name) + ;; This is a small shortcut to optimize analysis of typing code. _ (if (and (clojure.lang.Util/identical &type/Type endo-type) (clojure.lang.Util/identical &type/Type exo-type)) (return nil) (&type/check exo-type endo-type)) _cursor &/cursor] (return (&/|list (&&/|meta endo-type _cursor - (&&/$var (&/$Global (&/T [r-module r-name]))) - ))))) + (&&/$var (&/$Global (&/T [r-module r-name])))))))) (defn ^:private analyse-local [analyse exo-type name] (fn [state] diff --git a/src/lux/analyser/module.clj b/src/lux/analyser/module.clj index f35334399..ead4ffc67 100644 --- a/src/lux/analyser/module.clj +++ b/src/lux/analyser/module.clj @@ -159,31 +159,44 @@ nil)))) )) +(defn ^:private imports? [state imported-module source-module] + (->> state + (&/get$ &/$modules) + (&/|get source-module) + (&/get$ $imports) + (&/|any? (partial = imported-module)))) + (defn find-def [module name] (|do [current-module &/get-module-name] (fn [state] - (if-let [$module (->> state (&/get$ &/$modules) (&/|get module))] - (if-let [$def (->> $module (&/get$ $defs) (&/|get name))] - (|let [[?type ?meta ?value] $def] - (if (.equals ^Object current-module module) - (|case (&meta/meta-get &meta/alias-tag ?meta) - (&/$Some (&/$IdentM [?r-module ?r-name])) - ((find-def ?r-module ?r-name) - state) - - _ - (return* state (&/T [(&/T [module name]) $def]))) - (|case (&meta/meta-get &meta/export?-tag ?meta) - (&/$Some (&/$BoolM true)) - (return* state (&/T [(&/T [module name]) $def])) - - _ - ((&/fail-with-loc (str "[Analyser Error] Can't use unexported definition: " (str module &/+name-separator+ name))) - state)))) - ((&/fail-with-loc (str "[Analyser Error] Definition does not exist: " (str module &/+name-separator+ name))) + (if (or (= "lux" module) + (= current-module module) + (imports? state module current-module)) + (if-let [$module (->> state (&/get$ &/$modules) (&/|get module))] + (if-let [$def (->> $module (&/get$ $defs) (&/|get name))] + (|let [[?type ?meta ?value] $def] + (if (.equals ^Object current-module module) + (|case (&meta/meta-get &meta/alias-tag ?meta) + (&/$Some (&/$IdentM [?r-module ?r-name])) + ((find-def ?r-module ?r-name) + state) + + _ + (return* state (&/T [(&/T [module name]) $def]))) + (|case (&meta/meta-get &meta/export?-tag ?meta) + (&/$Some (&/$BoolM true)) + (return* state (&/T [(&/T [module name]) $def])) + + _ + ((&/fail-with-loc (str "[Analyser Error] Can't use unexported definition: " (str module &/+name-separator+ name))) + state)))) + ((&/fail-with-loc (str "[Analyser Error] Definition does not exist: " (str module &/+name-separator+ name))) + state)) + ((&/fail-with-loc (str "[Analyser Error] Module doesn't exist: " module)) state)) - ((&/fail-with-loc (str "[Analyser Error] Module doesn't exist: " module)) - state))))) + ((&/fail-with-loc (str "[Analyser Error] Unknown module: " module)) + state)) + ))) (defn ensure-type-def [def-data] "(-> DefData (Lux Type))" diff --git a/src/lux/base.clj b/src/lux/base.clj index 462bccd69..3c4438c63 100644 --- a/src/lux/base.clj +++ b/src/lux/base.clj @@ -70,6 +70,7 @@ ;; AST (defvariant ("BoolS" 1) + ("NatS" 1) ("IntS" 1) ("RealS" 1) ("CharS" 1) @@ -213,6 +214,7 @@ ;; Meta-data (defvariant ("BoolM" 1) + ("NatM" 1) ("IntM" 1) ("RealM" 1) ("CharM" 1) @@ -1044,6 +1046,9 @@ [_ ($BoolS ?value)] (pr-str ?value) + [_ ($NatS ?value)] + (str "+" (Long/toUnsignedString ?value)) + [_ ($IntS ?value)] (pr-str ?value) @@ -1239,7 +1244,7 @@ <default> ($Cons x xs*) - (<op> (p x) (|every? p xs*)))) + (<op> (p x) (<name> p xs*)))) |every? true and |any? false or) diff --git a/src/lux/compiler.clj b/src/lux/compiler.clj index 4548e71ab..294f2dc63 100644 --- a/src/lux/compiler.clj +++ b/src/lux/compiler.clj @@ -52,6 +52,9 @@ (&o/$bool ?value) (&&lux/compile-bool ?value) + (&o/$nat ?value) + (&&lux/compile-nat ?value) + (&o/$int ?value) (&&lux/compile-int ?value) @@ -91,6 +94,12 @@ (&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 ?arity ?scope ?env ?body) (&&lambda/compile-function compile-expression &/$None ?arity ?scope ?env ?body) diff --git a/src/lux/compiler/case.clj b/src/lux/compiler/case.clj index 08624d171..4ca543b8e 100644 --- a/src/lux/compiler/case.clj +++ b/src/lux/compiler/case.clj @@ -72,6 +72,16 @@ (.visitLdcInsn _value) (.visitJumpInsn Opcodes/IF_ICMPNE $else)) + (&o/$NatPM _value) + (doto writer + (.visitInsn Opcodes/DUP) + (.visitMethodInsn Opcodes/INVOKESTATIC "lux/LuxRT" "pm_stack_peek" "([Ljava/lang/Object;)Ljava/lang/Object;") + (.visitTypeInsn Opcodes/CHECKCAST "java/lang/Long") + (.visitMethodInsn Opcodes/INVOKEVIRTUAL "java/lang/Long" "longValue" "()J") + (.visitLdcInsn (long _value)) + (.visitInsn Opcodes/LCMP) + (.visitJumpInsn Opcodes/IFNE $else)) + (&o/$IntPM _value) (doto writer (.visitInsn Opcodes/DUP) diff --git a/src/lux/compiler/host.clj b/src/lux/compiler/host.clj index ae4b98f9f..cdd17a1ee 100644 --- a/src/lux/compiler/host.clj +++ b/src/lux/compiler/host.clj @@ -779,26 +779,109 @@ (.visitInsn Opcodes/RETURN) (.visitMaxs 0 0) (.visitEnd)) + _ (doto (.visitMethod =class (+ Opcodes/ACC_PUBLIC Opcodes/ACC_STATIC) "log" "(Ljava/lang/Object;)Ljava/lang/Object;" nil nil) + (.visitCode) + (.visitFieldInsn Opcodes/GETSTATIC "java/lang/System" "out" "Ljava/io/PrintStream;") + (.visitLdcInsn "LOG: ") + (.visitMethodInsn Opcodes/INVOKEVIRTUAL "java/io/PrintStream" "print" "(Ljava/lang/Object;)V") + (.visitFieldInsn Opcodes/GETSTATIC "java/lang/System" "out" "Ljava/io/PrintStream;") + (.visitVarInsn Opcodes/ALOAD 0) + (.visitMethodInsn Opcodes/INVOKEVIRTUAL "java/io/PrintStream" "println" "(Ljava/lang/Object;)V") + (.visitVarInsn Opcodes/ALOAD 0) + (.visitInsn Opcodes/ARETURN) + (.visitMaxs 0 0) + (.visitEnd)) + _ (doto (.visitMethod =class (+ Opcodes/ACC_PUBLIC Opcodes/ACC_STATIC) "make_none" "()Ljava/lang/Object;" nil nil) + (.visitCode) + (.visitLdcInsn (->> #'&/$None meta ::&/idx int)) ;; I + (.visitInsn Opcodes/ACONST_NULL) ;; I? + (.visitLdcInsn &/unit-tag) ;; I?U + (.visitMethodInsn Opcodes/INVOKESTATIC "lux/LuxRT" "sum_make" "(ILjava/lang/Object;Ljava/lang/Object;)[Ljava/lang/Object;") + (.visitInsn Opcodes/ARETURN) + (.visitMaxs 0 0) + (.visitEnd)) + _ (doto (.visitMethod =class (+ Opcodes/ACC_PUBLIC Opcodes/ACC_STATIC) "make_some" "(Ljava/lang/Object;)Ljava/lang/Object;" nil nil) + (.visitCode) + (.visitLdcInsn (->> #'&/$Some meta ::&/idx int)) ;; I + (.visitInsn Opcodes/ACONST_NULL) ;; I? + (.visitVarInsn Opcodes/ALOAD 0) ;; I?O + (.visitMethodInsn Opcodes/INVOKESTATIC "lux/LuxRT" "sum_make" "(ILjava/lang/Object;Ljava/lang/Object;)[Ljava/lang/Object;") + (.visitInsn Opcodes/ARETURN) + (.visitMaxs 0 0) + (.visitEnd)) _ (let [$end (new Label) - $else (new Label)] - (doto (.visitMethod =class (+ Opcodes/ACC_PUBLIC Opcodes/ACC_STATIC) "log" "(Ljava/lang/Object;)Ljava/lang/Object;" nil nil) + ;; $then (new Label) + $else (new Label) + $from (new Label) + $to (new Label) + $handler (new Label)] + (doto (.visitMethod =class (+ Opcodes/ACC_PUBLIC Opcodes/ACC_STATIC) "decode_nat" "(Ljava/lang/String;)Ljava/lang/Object;" nil nil) (.visitCode) - (.visitFieldInsn Opcodes/GETSTATIC "java/lang/System" "out" "Ljava/io/PrintStream;") - (.visitLdcInsn "LOG: ") - (.visitMethodInsn Opcodes/INVOKEVIRTUAL "java/io/PrintStream" "print" "(Ljava/lang/Object;)V") - (.visitFieldInsn Opcodes/GETSTATIC "java/lang/System" "out" "Ljava/io/PrintStream;") (.visitVarInsn Opcodes/ALOAD 0) - (.visitMethodInsn Opcodes/INVOKEVIRTUAL "java/io/PrintStream" "println" "(Ljava/lang/Object;)V") + (.visitLdcInsn "+") + (.visitMethodInsn Opcodes/INVOKEVIRTUAL "java/lang/String" "startsWith" "(Ljava/lang/String;)Z") + (.visitJumpInsn Opcodes/IFEQ $else) (.visitVarInsn Opcodes/ALOAD 0) + (.visitLdcInsn (int 1)) + (.visitMethodInsn Opcodes/INVOKEVIRTUAL "java/lang/String" "substring" "(I)Ljava/lang/String;") + (.visitTryCatchBlock $from $to $handler "java/lang/Exception") + (.visitLabel $from) + (.visitLdcInsn ",|_") + (.visitLdcInsn "") + (.visitMethodInsn Opcodes/INVOKEVIRTUAL "java/lang/String" "replaceAll" "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;") + (.visitMethodInsn Opcodes/INVOKESTATIC "java/lang/Long" "parseUnsignedLong" "(Ljava/lang/String;)J") + (.visitLabel $to) + ;; (.visitJumpInsn Opcodes/GOTO $then) + ;; (.visitLabel $then) + (&&/wrap-long) + (.visitMethodInsn Opcodes/INVOKESTATIC "lux/LuxRT" "make_some" "(Ljava/lang/Object;)Ljava/lang/Object;") + (.visitFrame Opcodes/F_NEW 1 (to-array ["java/lang/String"]) 1 (to-array ["java/lang/Object"])) + (.visitJumpInsn Opcodes/GOTO $end) + (.visitLabel $handler) + (.visitFrame Opcodes/F_NEW 1 (to-array ["java/lang/String"]) 1 (to-array ["java/lang/Exception"])) + (.visitInsn Opcodes/POP) + (.visitMethodInsn Opcodes/INVOKESTATIC "lux/LuxRT" "make_none" "()Ljava/lang/Object;") + (.visitFrame Opcodes/F_NEW 1 (to-array ["java/lang/String"]) 1 (to-array ["java/lang/Object"])) + (.visitJumpInsn Opcodes/GOTO $end) + (.visitLabel $else) + (.visitFrame Opcodes/F_NEW 1 (to-array ["java/lang/String"]) 0 (to-array [])) + (.visitMethodInsn Opcodes/INVOKESTATIC "lux/LuxRT" "make_none" "()Ljava/lang/Object;") + (.visitLabel $end) + (.visitFrame Opcodes/F_NEW 1 (to-array ["java/lang/String"]) 1 (to-array ["java/lang/Object"])) (.visitInsn Opcodes/ARETURN) (.visitMaxs 0 0) (.visitEnd))) _ (doto =class - (compile-LuxRT-adt-methods) - (compile-LuxRT-pm-methods))]] + (compile-LuxRT-pm-methods) + (compile-LuxRT-adt-methods))]] (&&/save-class! (second (string/split &&/lux-utils-class #"/")) (.toByteArray (doto =class .visitEnd))))) +(defn ^:private compile-jvm-try [compile ?values special-args] + (|do [:let [(&/$Cons ?body (&/$Cons ?catch (&/$Nil))) ?values + ;; (&/$Nil) special-args + ] + ^MethodVisitor *writer* &/get-writer + :let [$from (new Label) + $to (new Label) + $handler (new Label) + $end (new Label)] + :let [_ (doto *writer* + (.visitTryCatchBlock $from $to $handler "java/lang/Exception") + (.visitLabel $from))] + _ (compile ?body) + :let [_ (doto *writer* + (.visitJumpInsn Opcodes/GOTO $end) + (.visitLabel $to) + (.visitLabel $handler))] + _ (compile ?catch) + :let [_ (doto *writer* + (.visitTypeInsn Opcodes/CHECKCAST &&/function-class) + (.visitInsn Opcodes/SWAP) + (.visitMethodInsn Opcodes/INVOKEVIRTUAL &&/function-class &&/apply-method (&&/apply-signature 1)))] + :let [_ (.visitLabel *writer* $end)]] + (return nil))) + (do-template [<name> <op> <from-class> <from-method> <from-sig> <to-class> <to-sig>] (defn <name> [compile _?value special-args] (|do [:let [(&/$Cons ?value (&/$Nil)) _?value] @@ -814,29 +897,35 @@ (.visitMethodInsn Opcodes/INVOKESPECIAL (&host-generics/->bytecode-class-name <to-class>) init-method <to-sig>))]] (return nil))) - ^:private compile-jvm-d2f Opcodes/D2F "java.lang.Double" "doubleValue" "()D" "java.lang.Float" "(F)V" - ^:private compile-jvm-d2i Opcodes/D2I "java.lang.Double" "doubleValue" "()D" "java.lang.Integer" "(I)V" - ^:private compile-jvm-d2l Opcodes/D2L "java.lang.Double" "doubleValue" "()D" "java.lang.Long" "(J)V" - - ^:private compile-jvm-f2d Opcodes/F2D "java.lang.Float" "floatValue" "()F" "java.lang.Double" "(D)V" - ^:private compile-jvm-f2i Opcodes/F2I "java.lang.Float" "floatValue" "()F" "java.lang.Integer" "(I)V" - ^:private compile-jvm-f2l Opcodes/F2L "java.lang.Float" "floatValue" "()F" "java.lang.Long" "(J)V" - - ^:private compile-jvm-i2b Opcodes/I2B "java.lang.Integer" "intValue" "()I" "java.lang.Byte" "(B)V" - ^:private compile-jvm-i2c Opcodes/I2C "java.lang.Integer" "intValue" "()I" "java.lang.Character" "(C)V" - ^:private compile-jvm-i2d Opcodes/I2D "java.lang.Integer" "intValue" "()I" "java.lang.Double" "(D)V" - ^:private compile-jvm-i2f Opcodes/I2F "java.lang.Integer" "intValue" "()I" "java.lang.Float" "(F)V" - ^:private compile-jvm-i2l Opcodes/I2L "java.lang.Integer" "intValue" "()I" "java.lang.Long" "(J)V" - ^:private compile-jvm-i2s Opcodes/I2S "java.lang.Integer" "intValue" "()I" "java.lang.Short" "(S)V" - - ^:private compile-jvm-l2d Opcodes/L2D "java.lang.Long" "longValue" "()J" "java.lang.Double" "(D)V" - ^:private compile-jvm-l2f Opcodes/L2F "java.lang.Long" "longValue" "()J" "java.lang.Float" "(F)V" - ^:private compile-jvm-l2i Opcodes/L2I "java.lang.Long" "longValue" "()J" "java.lang.Integer" "(I)V" - - ^:private compile-jvm-c2b Opcodes/I2B "java.lang.Character" "charValue" "()C" "java.lang.Byte" "(B)V" - ^:private compile-jvm-c2s Opcodes/I2S "java.lang.Character" "charValue" "()C" "java.lang.Short" "(S)V" - ^:private compile-jvm-c2i Opcodes/NOP "java.lang.Character" "charValue" "()C" "java.lang.Integer" "(I)V" - ^:private compile-jvm-c2l Opcodes/I2L "java.lang.Character" "charValue" "()C" "java.lang.Long" "(J)V" + ^:private compile-jvm-d2f Opcodes/D2F "java.lang.Double" "doubleValue" "()D" "java.lang.Float" "(F)V" + ^:private compile-jvm-d2i Opcodes/D2I "java.lang.Double" "doubleValue" "()D" "java.lang.Integer" "(I)V" + ^:private compile-jvm-d2l Opcodes/D2L "java.lang.Double" "doubleValue" "()D" "java.lang.Long" "(J)V" + + ^:private compile-jvm-f2d Opcodes/F2D "java.lang.Float" "floatValue" "()F" "java.lang.Double" "(D)V" + ^:private compile-jvm-f2i Opcodes/F2I "java.lang.Float" "floatValue" "()F" "java.lang.Integer" "(I)V" + ^:private compile-jvm-f2l Opcodes/F2L "java.lang.Float" "floatValue" "()F" "java.lang.Long" "(J)V" + + ^:private compile-jvm-i2b Opcodes/I2B "java.lang.Integer" "intValue" "()I" "java.lang.Byte" "(B)V" + ^:private compile-jvm-i2c Opcodes/I2C "java.lang.Integer" "intValue" "()I" "java.lang.Character" "(C)V" + ^:private compile-jvm-i2d Opcodes/I2D "java.lang.Integer" "intValue" "()I" "java.lang.Double" "(D)V" + ^:private compile-jvm-i2f Opcodes/I2F "java.lang.Integer" "intValue" "()I" "java.lang.Float" "(F)V" + ^:private compile-jvm-i2l Opcodes/I2L "java.lang.Integer" "intValue" "()I" "java.lang.Long" "(J)V" + ^:private compile-jvm-i2s Opcodes/I2S "java.lang.Integer" "intValue" "()I" "java.lang.Short" "(S)V" + + ^:private compile-jvm-l2d Opcodes/L2D "java.lang.Long" "longValue" "()J" "java.lang.Double" "(D)V" + ^:private compile-jvm-l2f Opcodes/L2F "java.lang.Long" "longValue" "()J" "java.lang.Float" "(F)V" + ^:private compile-jvm-l2i Opcodes/L2I "java.lang.Long" "longValue" "()J" "java.lang.Integer" "(I)V" + ^:private compile-jvm-l2s Opcodes/L2I "java.lang.Long" "longValue" "()J" "java.lang.Short" "(S)V" + ^:private compile-jvm-l2b Opcodes/L2I "java.lang.Long" "longValue" "()J" "java.lang.Byte" "(B)V" + + ^:private compile-jvm-c2b Opcodes/I2B "java.lang.Character" "charValue" "()C" "java.lang.Byte" "(B)V" + ^:private compile-jvm-c2s Opcodes/I2S "java.lang.Character" "charValue" "()C" "java.lang.Short" "(S)V" + ^:private compile-jvm-c2i Opcodes/NOP "java.lang.Character" "charValue" "()C" "java.lang.Integer" "(I)V" + ^:private compile-jvm-c2l Opcodes/I2L "java.lang.Character" "charValue" "()C" "java.lang.Long" "(J)V" + + ^:private compile-jvm-s2l Opcodes/I2L "java.lang.Short" "shortValue" "()S" "java.lang.Long" "(J)V" + + ^:private compile-jvm-b2l Opcodes/I2L "java.lang.Byte" "byteValue" "()B" "java.lang.Long" "(J)V" ) (do-template [<name> <op> <from1-method> <from1-sig> <from1-class> <from2-method> <from2-sig> <from2-class> <to-class> <to-sig>] @@ -1406,6 +1495,133 @@ (.visitLabel $end))]] (return nil))) +(do-template [<name> <opcode> <wrapper-class> <value-method> <value-method-sig> <wrap>] + (defn <name> [compile ?values special-args] + (|do [:let [(&/$Cons ?x (&/$Cons ?y (&/$Nil))) ?values] + :let [+wrapper-class+ (&host-generics/->bytecode-class-name <wrapper-class>)] + ^MethodVisitor *writer* &/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>) + (<wrap>))]] + (return nil))) + + ^:private compile-nat-add Opcodes/LADD "java.lang.Long" "longValue" "()J" &&/wrap-long + ^:private compile-nat-sub Opcodes/LSUB "java.lang.Long" "longValue" "()J" &&/wrap-long + ^:private compile-nat-mul Opcodes/LMUL "java.lang.Long" "longValue" "()J" &&/wrap-long + ) + +(do-template [<name> <wrapper-class> <value-method> <value-method-sig> <wrap> <comp-method> <comp-sig>] + (defn <name> [compile ?values special-args] + (|do [:let [(&/$Cons ?x (&/$Cons ?y (&/$Nil))) ?values] + :let [+wrapper-class+ (&host-generics/->bytecode-class-name <wrapper-class>)] + ^MethodVisitor *writer* &/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* + (.visitMethodInsn Opcodes/INVOKESTATIC +wrapper-class+ <comp-method> <comp-sig>) + (&&/wrap-long))]] + (return nil))) + + ^:private compile-nat-div "java.lang.Long" "longValue" "()J" &&/wrap-long "divideUnsigned" "(JJ)J" + ^:private compile-nat-rem "java.lang.Long" "longValue" "()J" &&/wrap-long "remainderUnsigned" "(JJ)J" + ) + +(do-template [<name> <cmpcode> <cmp-output> <wrapper-class> <value-method> <value-method-sig> <comp-method> <comp-sig>] + (defn <name> [compile ?values special-args] + (|do [:let [(&/$Cons ?x (&/$Cons ?y (&/$Nil))) ?values] + :let [+wrapper-class+ (&host-generics/->bytecode-class-name <wrapper-class>)] + ^MethodVisitor *writer* &/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>)) + $then (new Label) + $end (new Label) + _ (doto *writer* + (.visitMethodInsn Opcodes/INVOKESTATIC +wrapper-class+ "compareUnsigned" "(JJ)I") + (.visitLdcInsn (int <cmp-output>)) + (.visitJumpInsn Opcodes/IF_ICMPEQ $then) + (.visitFieldInsn Opcodes/GETSTATIC (&host-generics/->bytecode-class-name "java.lang.Boolean") "FALSE" (&host-generics/->type-signature "java.lang.Boolean")) + (.visitJumpInsn Opcodes/GOTO $end) + (.visitLabel $then) + (.visitFieldInsn Opcodes/GETSTATIC (&host-generics/->bytecode-class-name "java.lang.Boolean") "TRUE" (&host-generics/->type-signature "java.lang.Boolean")) + (.visitLabel $end))]] + (return nil))) + + ^:private compile-nat-eq Opcodes/LCMP 0 "java.lang.Long" "longValue" "()J" "compareUnsigned" "(JJ)I" + ^:private compile-nat-lt Opcodes/LCMP -1 "java.lang.Long" "longValue" "()J" "compareUnsigned" "(JJ)I" + ) + +(defn ^:private compile-nat-encode [compile ?values special-args] + (|do [:let [(&/$Cons ?x (&/$Nil)) ?values] + :let [+wrapper-class+ (&host-generics/->bytecode-class-name "java.lang.Long")] + ^MethodVisitor *writer* &/get-writer + _ (compile ?x) + :let [_ (doto *writer* + (.visitTypeInsn Opcodes/CHECKCAST +wrapper-class+) + (.visitMethodInsn Opcodes/INVOKEVIRTUAL +wrapper-class+ "longValue" "()J"))] + :let [_ (doto *writer* + (.visitMethodInsn Opcodes/INVOKESTATIC +wrapper-class+ "toUnsignedString" "(J)Ljava/lang/String;") + (.visitLdcInsn "+") + (.visitInsn Opcodes/SWAP) + (.visitMethodInsn Opcodes/INVOKEVIRTUAL "java/lang/String" "concat" "(Ljava/lang/String;)Ljava/lang/String;"))]] + (return nil))) + +(defn ^:private compile-nat-decode [compile ?values special-args] + (|do [:let [(&/$Cons ?x (&/$Nil)) ?values] + :let [+wrapper-class+ (&host-generics/->bytecode-class-name "java.lang.String")] + ^MethodVisitor *writer* &/get-writer + _ (compile ?x) + :let [_ (doto *writer* + (.visitTypeInsn Opcodes/CHECKCAST +wrapper-class+))] + :let [_ (doto *writer* + (.visitMethodInsn Opcodes/INVOKESTATIC "lux/LuxRT" "decode_nat" "(Ljava/lang/String;)Ljava/lang/Object;"))]] + (return nil))) + +(do-template [<name> <instr> <wrapper>] + (defn <name> [compile ?values special-args] + (|do [:let [(&/$Nil) ?values] + ^MethodVisitor *writer* &/get-writer + :let [_ (doto *writer* + <instr> + <wrapper>)]] + (return nil))) + + ^:private compile-nat-min-value (.visitLdcInsn 0) &&/wrap-long + ^:private compile-nat-max-value (.visitLdcInsn -1) &&/wrap-long + ) + +(do-template [<name>] + (defn <name> [compile ?values special-args] + (|do [:let [(&/$Cons ?x (&/$Nil)) ?values] + ^MethodVisitor *writer* &/get-writer + _ (compile ?x)] + (return nil))) + + ^:private compile-nat-to-int + ^:private compile-int-to-nat + ^:private compile-nat-to-char + ^:private compile-char-to-nat + ) + (defn compile-host [compile proc-category proc-name ?values special-args] (case proc-category "lux" @@ -1425,6 +1641,33 @@ "array" (case proc-name "get" (compile-array-get compile ?values special-args)) + + "nat" + (case proc-name + "add" (compile-nat-add compile ?values special-args) + "sub" (compile-nat-sub compile ?values special-args) + "mul" (compile-nat-mul compile ?values special-args) + "div" (compile-nat-div compile ?values special-args) + "rem" (compile-nat-rem compile ?values special-args) + "eq" (compile-nat-eq compile ?values special-args) + "lt" (compile-nat-lt compile ?values special-args) + "encode" (compile-nat-encode compile ?values special-args) + "decode" (compile-nat-decode compile ?values special-args) + "max-value" (compile-nat-max-value compile ?values special-args) + "min-value" (compile-nat-min-value compile ?values special-args) + "to-int" (compile-nat-to-int compile ?values special-args) + "to-char" (compile-nat-to-char compile ?values special-args) + ) + + "int" + (case proc-name + "to-nat" (compile-int-to-nat compile ?values special-args) + ) + + "char" + (case proc-name + "to-nat" (compile-char-to-nat compile ?values special-args) + ) "jvm" (case proc-name @@ -1519,10 +1762,14 @@ "l2d" (compile-jvm-l2d compile ?values special-args) "l2f" (compile-jvm-l2f compile ?values special-args) "l2i" (compile-jvm-l2i compile ?values special-args) + "l2s" (compile-jvm-l2s compile ?values special-args) + "l2b" (compile-jvm-l2b compile ?values special-args) "c2b" (compile-jvm-c2b compile ?values special-args) "c2s" (compile-jvm-c2s compile ?values special-args) "c2i" (compile-jvm-c2i compile ?values special-args) "c2l" (compile-jvm-c2l compile ?values special-args) + "s2l" (compile-jvm-s2l compile ?values special-args) + "b2l" (compile-jvm-b2l compile ?values special-args) ;; else (fail (str "[Compiler Error] Unknown host procedure: " [proc-category proc-name]))) diff --git a/src/lux/compiler/lux.clj b/src/lux/compiler/lux.clj index 87113b538..ba031eda7 100644 --- a/src/lux/compiler/lux.clj +++ b/src/lux/compiler/lux.clj @@ -45,6 +45,7 @@ (.visitMethodInsn Opcodes/INVOKESPECIAL <class> "<init>" <sig>))]] (return nil))) + compile-nat "java/lang/Long" "(J)V" long compile-int "java/lang/Long" "(J)V" long compile-real "java/lang/Double" "(D)V" double compile-char "java/lang/Character" "(C)V" char @@ -188,6 +189,36 @@ _ (compile _body)] (return nil))) +(defn compile-record-get [compile _value _path] + (|do [^MethodVisitor *writer* &/get-writer + _ (compile _value) + :let [_ (&/|map (fn [step] + (|let [[idx tail?] step] + (doto *writer* + (.visitTypeInsn Opcodes/CHECKCAST "[Ljava/lang/Object;") + (.visitLdcInsn (int idx)) + (.visitMethodInsn Opcodes/INVOKESTATIC "lux/LuxRT" + (if tail? "product_getRight" "product_getLeft") + "([Ljava/lang/Object;I)Ljava/lang/Object;")))) + _path)]] + (return nil))) + +(defn compile-if [compile _test _then _else] + (|do [^MethodVisitor *writer* &/get-writer + _ (compile _test) + :let [$else (new Label) + $end (new Label) + _ (doto *writer* + &&/unwrap-boolean + (.visitJumpInsn Opcodes/IFEQ $else))] + _ (compile _then) + :let [_ (.visitJumpInsn *writer* Opcodes/GOTO $end)] + :let [_ (.visitLabel *writer* $else)] + _ (compile _else) + :let [_ (.visitJumpInsn *writer* Opcodes/GOTO $end) + _ (.visitLabel *writer* $end)]] + (return nil))) + (defn ^:private compile-def-type [compile ?body] (|do [:let [?def-type (|case ?body [[?def-type ?def-cursor] (&a/$ann ?def-value ?type-expr ?def-value-type)] diff --git a/src/lux/compiler/type.clj b/src/lux/compiler/type.clj index bf6ec5539..f51165ea3 100644 --- a/src/lux/compiler/type.clj +++ b/src/lux/compiler/type.clj @@ -30,6 +30,7 @@ (<tag> value))) ^:private bool$ &a/$bool "(-> Bool Analysis)" + ^:private nat$ &a/$nat "(-> Nat Analysis)" ^:private int$ &a/$int "(-> Int Analysis)" ^:private real$ &a/$real "(-> Real Analysis)" ^:private char$ &a/$char "(-> Char Analysis)" @@ -109,6 +110,9 @@ (&/$BoolM value) (variant$ #'&/$BoolM (bool$ value)) + (&/$NatM value) + (variant$ #'&/$NatM (nat$ value)) + (&/$IntM value) (variant$ #'&/$IntM (int$ value)) diff --git a/src/lux/host.clj b/src/lux/host.clj index 213a68cea..de8c6cca1 100644 --- a/src/lux/host.clj +++ b/src/lux/host.clj @@ -88,14 +88,14 @@ (do-template [<name> <static?>] (defn <name> [class-loader target field] - (|let [target-class (Class/forName (&host-type/as-obj target) true class-loader)] + (|let [target-class (Class/forName target true class-loader)] (if-let [^Type gtype (first (for [^Field =field (seq (.getDeclaredFields target-class)) :when (and (.equals ^Object field (.getName =field)) (.equals ^Object <static?> (Modifier/isStatic (.getModifiers =field))))] (.getGenericType =field)))] (|let [gvars (->> target-class .getTypeParameters seq &/->list)] (return (&/T [gvars gtype]))) - (fail (str "[Host Error] Field does not exist: " target "." field))))) + (&/fail-with-loc (str "[Host Error] Field does not exist: " target "." field))))) lookup-static-field true lookup-field false @@ -103,33 +103,38 @@ (do-template [<name> <static?> <method-type>] (defn <name> [class-loader target method-name args] - (|let [target-class (Class/forName (&host-type/as-obj target) true class-loader)] - (if-let [^Method method (first (for [^Method =method (.getDeclaredMethods target-class) - :when (and (.equals ^Object method-name (.getName =method)) - (.equals ^Object <static?> (Modifier/isStatic (.getModifiers =method))) - (let [param-types (&/->list (seq (.getParameterTypes =method)))] - (and (= (&/|length args) (&/|length param-types)) - (&/fold2 #(and %1 (.equals ^Object %2 %3)) - true - args - (&/|map #(.getName ^Class %) param-types)))))] - =method))] - (|let [parent-gvars (->> target-class .getTypeParameters seq &/->list) - gvars (->> method .getTypeParameters seq &/->list) - gargs (->> method .getGenericParameterTypes seq &/->list)] - (return (&/T [(.getGenericReturnType method) - (->> method .getExceptionTypes &/->list (&/|map #(.getName ^Class %))) - parent-gvars - gvars - gargs]))) - (fail (str "[Host Error] " <method-type> " method does not exist: " target "." method-name))))) + (|let [target-class (Class/forName target true class-loader)] + (if-let [[^Method method ^Class declarer] (first (for [^Method =method (.getMethods target-class) + :when (and (.equals ^Object method-name (.getName =method)) + (.equals ^Object <static?> (Modifier/isStatic (.getModifiers =method))) + (let [param-types (&/->list (seq (.getParameterTypes =method)))] + (and (= (&/|length args) (&/|length param-types)) + (&/fold2 #(and %1 (.equals ^Object %2 %3)) + true + args + (&/|map #(.getName ^Class %) param-types)))))] + [=method + (.getDeclaringClass =method)]))] + (if (= target-class declarer) + (|let [parent-gvars (->> target-class .getTypeParameters seq &/->list) + gvars (->> method .getTypeParameters seq &/->list) + gargs (->> method .getGenericParameterTypes seq &/->list) + _ (when (.getAnnotation method java.lang.Deprecated) + (println (str "[Host Warning] Deprecated method: " target "." method-name " " (->> args &/->seq print-str))))] + (return (&/T [(.getGenericReturnType method) + (->> method .getExceptionTypes &/->list (&/|map #(.getName ^Class %))) + parent-gvars + gvars + gargs]))) + (&/fail-with-loc (str "[Host Error] " <method-type> " method " (pr-str method-name) " for " "(" (->> args (&/|interpose ", ") (&/fold str "")) ")" " belongs to parent " (.getName declarer) " instead of " target))) + (&/fail-with-loc (str "[Host Error] " <method-type> " method does not exist: " target "." method-name " " "(" (->> args (&/|interpose ", ") (&/fold str "")) ")"))))) lookup-static-method true "Static" lookup-virtual-method false "Virtual" ) (defn lookup-constructor [class-loader target args] - (let [target-class (Class/forName (&host-type/as-obj target) true class-loader)] + (let [target-class (Class/forName target true class-loader)] (if-let [^Constructor ctor (first (for [^Constructor =method (.getDeclaredConstructors target-class) :when (let [param-types (&/->list (seq (.getParameterTypes =method)))] (and (= (&/|length args) (&/|length param-types)) @@ -140,14 +145,16 @@ =method))] (|let [gvars (->> target-class .getTypeParameters seq &/->list) gargs (->> ctor .getGenericParameterTypes seq &/->list) - exs (->> ctor .getExceptionTypes &/->list (&/|map #(.getName ^Class %)))] + exs (->> ctor .getExceptionTypes &/->list (&/|map #(.getName ^Class %))) + _ (when (.getAnnotation ctor java.lang.Deprecated) + (println (str "[Host Warning] Deprecated constructor: " target " " (->> args &/->seq print-str))))] (return (&/T [exs gvars gargs]))) - (&/fail-with-loc (str "[Host Error] Constructor does not exist: " target))))) + (&/fail-with-loc (str "[Host Error] Constructor does not exist: " target " " (->> args &/->seq print-str)))))) (defn abstract-methods [class-loader super-class] "(-> ClassLoader SuperClassDecl (Lux (List (, Text (List Text)))))" (|let [[super-name super-params] super-class] - (return (&/->list (for [^Method =method (.getDeclaredMethods (Class/forName (&host-type/as-obj super-name) true class-loader)) + (return (&/->list (for [^Method =method (.getDeclaredMethods (Class/forName super-name true class-loader)) :when (Modifier/isAbstract (.getModifiers =method))] (&/T [(.getName =method) (&/|map #(.getName ^Class %) (&/->list (seq (.getParameterTypes =method))))])))))) diff --git a/src/lux/lexer.clj b/src/lux/lexer.clj index 9754456b9..f52823bfc 100644 --- a/src/lux/lexer.clj +++ b/src/lux/lexer.clj @@ -15,6 +15,7 @@ ("White_Space" 1) ("Comment" 1) ("Bool" 1) + ("Nat" 1) ("Int" 1) ("Real" 1) ("Char" 1) @@ -157,8 +158,16 @@ (return (&/T [meta (<tag> token)])))) lex-bool $Bool #"^(true|false)" - lex-int $Int #"^-?(0|[1-9][0-9]*)" - lex-real $Real #"^-?(0\.[0-9]+|[1-9][0-9]*\.[0-9]+)(e-?[1-9][0-9]*)?" + ) + +(do-template [<name> <tag> <regex>] + (def <name> + (|do [[meta _ token] (&reader/read-regex <regex>)] + (return (&/T [meta (<tag> (string/replace token #",|_" ""))])))) + + lex-nat $Nat #"^\+(0|[1-9][0-9,_]*)" + lex-int $Int #"^-?(0|[1-9][0-9,_]*)" + lex-real $Real #"^-?(0\.[0-9,_]+|[1-9][0-9,_]*\.[0-9,_]+)(e-?[1-9][0-9,_]*)?" ) (def lex-char @@ -233,6 +242,7 @@ lex-comment lex-bool lex-real + lex-nat lex-int lex-char lex-text diff --git a/src/lux/optimizer.clj b/src/lux/optimizer.clj index c03515370..56a73060c 100644 --- a/src/lux/optimizer.clj +++ b/src/lux/optimizer.clj @@ -9,7 +9,9 @@ ;; [Tags] (defvariant + ;; These tags just have a one-to-one correspondence with Analysis data-structures. ("bool" 1) + ("nat" 1) ("int" 1) ("real" 1) ("char" 1) @@ -24,27 +26,82 @@ ("captured" 3) ("proc" 3) - ;; Purely for optimizations + ;; These other tags represent higher-order constructs that manifest + ;; themselves as patterns in the code. + ;; Lux doesn't formally provide these features, but some macros + ;; expose ways to implement them in terms of the other (primitive) + ;; features. + ;; The optimizer looks for those usage patterns and transforms them + ;; into explicit constructs, which are then subject to specialized optimizations. + + ;; This is a loop, as expected in imperative programming. ("loop" 1) + ;; This is a simple let-expression, as opposed to the more general pattern-matching. ("let" 3) + ;; This is an access to a record's member. It can be multiple level: + ;; e.g. record.l1.l2.l3 + ;; The record-get token stores the path, for simpler compilation. + ("record-get" 2) + ;; Regular, run-of-the-mill if expressions. + ("if" 3) ) -;; For pattern-matching +;; [Utils] + +;; [[Pattern-Matching Traversal Optimization]] + +;; This represents an alternative way to view pattern-matching. +;; The PM that Lux provides has declarative semantics, with the user +;; specifying how his data is shaped, but not how to traverse it. +;; The optimizer's PM is operational in nature, and relies on +;; specifying a path of traversal, with a variety of operations that +;; can be done along the way. +;; The algorithm relies on looking at pattern-matching as traversing a +;; (possibly) branching path, where each step along the path +;; corresponds to a value, the ends of the path are the jumping-off +;; points for the bodies of branches, and branching decisions can be +;; backtracked, if they don't result in a valid jump. (defvariant + ;; Throw away the current data-node (CDN). It's useless. ("PopPM" 0) + ;; Store the CDN in a register. ("BindPM" 1) + ;; Compare the CDN with a boolean value. ("BoolPM" 1) + ;; Compare the CDN with a natural value. + ("NatPM" 1) + ;; Compare the CDN with an integer value. ("IntPM" 1) + ;; Compare the CDN with a real value. ("RealPM" 1) + ;; Compare the CDN with a character value. ("CharPM" 1) + ;; Compare the CDN with a text value. ("TextPM" 1) + ;; Compare the CDN with a variant value. If valid, proceed to test + ;; the variant's inner value. ("VariantPM" 1) + ;; Access a tuple value at a given index, for further examination. ("TuplePM" 1) + ;; Creates an instance of the backtracking info, as a preparatory + ;; step to exploring one of the branching paths. ("AltPM" 2) + ;; Allows to test the CDN, while keeping a copy of it for more + ;; tasting later on. + ;; If necessary when doing multiple tests on a single value, like + ;; when testing multiple parts of a tuple. ("SeqPM" 2) + ;; This is the jumping-off point for the PM part, where the PM + ;; data-structure is thrown away and the program jumps to the + ;; branch's body. ("ExecPM" 1)) -;; [Utils] +;; This function does a simple transformation from the declarative +;; model of PM of the analyser, to the operational model of PM of the +;; optimizer. +;; You may notice that all branches end in PopPM. +;; The reason is that testing does not immediately imply throwing away +;; the data to be tested, which is why a popping step must immediately follow. (defn ^:private transform-pm* [test] (|case test (&a-case/$NoTestAC) @@ -58,6 +115,10 @@ (&/|list ($BoolPM _value) $PopPM) + (&a-case/$NatTestAC _value) + (&/|list ($NatPM _value) + $PopPM) + (&a-case/$IntTestAC _value) (&/|list ($IntPM _value) $PopPM) @@ -83,12 +144,22 @@ (&a-case/$TupleTestAC _sub-tests) (|case _sub-tests + ;; An empty tuple corresponds to unit, which can't be tested in + ;; any meaningful way, so it's just popped. (&/$Nil) (&/|list $PopPM) + ;; A tuple of a single element is equivalent to the element + ;; itself, to the element's PM is generated. (&/$Cons _only-test (&/$Nil)) (transform-pm* _only-test) + ;; Single tuple PM features the tests of each tuple member + ;; inlined, it's operational equivalent is interleaving the + ;; access to each tuple member, followed by the testing of said + ;; member. + ;; That is way each sequence of access+subtesting gets generated + ;; and later they all get concatenated. _ (|let [tuple-size (&/|length _sub-tests)] (&/|++ (&/flat-map (fn [idx+test*] @@ -101,6 +172,15 @@ _sub-tests)) (&/|list $PopPM)))))) +;; It will be common for pattern-matching on a very nested +;; data-structure to require popping all the intermediate +;; data-structures that were visited once it's all done. +;; However, the PM infrastructure employs a single data-stack to keep +;; all data nodes in the trajectory, and that data-stack can just be +;; thrown again entirely, in just one step. +;; Because of that, any ending POPs prior to throwing away the +;; data-stack would be completely useless. +;; This function cleans them all up, to avoid wasteful computation later. (defn ^:private clean-unnecessary-pops [steps] (|case steps (&/$Cons ($PopPM) _steps) @@ -109,11 +189,19 @@ _ steps)) +;; This transforms a single branch of a PM tree into it's operational +;; equivalent, while also associating the PM of the branch with the +;; jump to the branch's body. (defn ^:private transform-pm [test body-id] (&/fold (fn [right left] ($SeqPM left right)) ($ExecPM body-id) (clean-unnecessary-pops (&/|reverse (transform-pm* test))))) +;; This function fuses together the paths of the PM traversal, adding +;; branching AltPMs where necessary, and fusing similar paths together +;; as much as possible, when early parts of them coincide. +;; The goal is to minimize rework as much as possible by sharing as +;; much of each path as possible. (defn ^:private fuse-pms [pre post] (|case (&/T [pre post]) [($PopPM) ($PopPM)] @@ -129,6 +217,11 @@ ($BoolPM _pre-value) ($AltPM pre post)) + [($NatPM _pre-value) ($NatPM _post-value)] + (if (= _pre-value _post-value) + ($NatPM _pre-value) + ($AltPM pre post)) + [($IntPM _pre-value) ($IntPM _post-value)] (if (= _pre-value _post-value) ($IntPM _pre-value) @@ -181,6 +274,8 @@ ($AltPM pre post) )) +;; This is the top-level function for optimizing PM, which transforms +;; each branch and then fuses them together. (defn ^:private optimize-pm [branches] (|let [;; branches (&/|reverse branches*) bodies (&/|map &/|second branches) @@ -199,6 +294,36 @@ bodies]) ))) +;; [[Function-Folding Optimization]] + +;; The semantics of Lux establish that all functions are of a single +;; argument and the multi-argument functions are actually nested +;; functions being generated and then applied. +;; This, of course, would generate a lot of waste. +;; To avoid it, Lux actually folds function definitions together, +;; thereby creating functions that can be used both +;; one-argument-at-a-time, and also being called with all, or just a +;; partial amount of their arguments. +;; This avoids generating too many artifacts during compilation, since +;; they get "compressed", and it can also lead to faster execution, by +;; enabling optimized function calls later. + +;; Functions and captured variables have "scopes", which tell which +;; function they are, or to which function they belong. +;; During the folding, inner functions dissapear, since their bodies +;; are merged into their outer "parent" functions. +;; Their scopes must change accordingy. +(defn ^:private de-scope [old-scope new-scope scope] + "(-> Scope Scope Scope Scope)" + (if (identical? new-scope scope) + old-scope + scope)) + +;; Also, it must be noted that when folding functions, the indexes of +;; the registers have to be changed accodingly. +;; That is what the following "shifting" functions are for. + +;; Shifts the registers for PM operations. (defn ^:private shift-pattern [pattern] (|case pattern ($BindPM _var-id) @@ -214,12 +339,7 @@ pattern )) -(defn ^:private de-scope [old-scope new-scope scope] - "(-> Scope Scope Scope Scope)" - (if (identical? new-scope scope) - old-scope - scope)) - +;; Shifts the body of a function after a folding is performed. (defn shift-function-body [old-scope new-scope own-body? body] "(-> Scope Scope Bool Optimized Optimized)" (|let [[meta body-] body] @@ -266,6 +386,7 @@ body) body) + ;; This special "apply" rule is for handling better recursive calls. ($apply [meta-0 ($var (&/$Local 0))] args) (if own-body? (&/T [meta ($apply (&/T [meta-0 ($var (&/$Local 0))]) @@ -300,11 +421,70 @@ (inc _register) _register) (shift-function-body old-scope new-scope own-body? _body))]) + + ($record-get _value _path) + (&/T [meta ($record-get (shift-function-body old-scope new-scope own-body? _value) + _path)]) + + ($if _test _then _else) + (&/T [meta ($if (shift-function-body old-scope new-scope own-body? _test) + (shift-function-body old-scope new-scope own-body? _then) + (shift-function-body old-scope new-scope own-body? _else))]) _ body ))) +;; [[Record-Manipulation Optimizations]] + +;; If a pattern-matching tree with a single branch is found, and that +;; branch corresponds to a tuple PM, and the body corresponds to a +;; local variable, it's likely that the local refers to some member of +;; the tuple that is being extracted. +;; That is the pattern that is to be expected of record read-access, +;; so this function tries to extract the (possibly nested) path +;; necessary, ending in the data-node of the wanted member. +(defn ^:private record-read-path [pms member-idx] + "(-> (List PM) Idx (List Idx))" + (loop [current-idx 0 + pms pms] + (|case pms + (&/$Nil) + &/$None + + (&/$Cons _pm _pms) + (|case _pm + (&a-case/$NoTestAC) + (recur (inc current-idx) + _pms) + + (&a-case/$StoreTestAC _register) + (if (= member-idx _register) + (&/|list (&/T [current-idx (&/|empty? _pms)])) + (recur (inc current-idx) + _pms)) + + (&a-case/$TupleTestAC _sub-tests) + (let [sub-path (record-read-path _sub-tests member-idx)] + (if (not (&/|empty? sub-path)) + (&/$Cons (&/T [current-idx (&/|empty? _pms)]) sub-path) + (recur (inc current-idx) + _pms) + )) + + _ + (&/|list)) + ))) + +;; [[Loop Optimizations]] + +;; Lux doesn't offer any looping constructs, relying instead on +;; recursion. +;; Some common usages of recursion can be written more efficiently +;; just using regular loops/iteration. +;; This optimization looks for tail-calls in the function body, +;; rewriting them as jumps to the beginning of the function, while +;; they also updated the necessary local variables for the next iteration. (defn ^:private optimize-loop [arity optim] "(-> Int Optimized Optimized)" (|let [[meta optim-] optim] @@ -330,6 +510,12 @@ optim ))) +;; [[Initial Optimization]] + +;; Before any big optimization can be done, the incoming Analysis nodes +;; must be transformed into Optimized nodes, amenable to further transformations. +;; This function does the job, while also detecting (and optimizing) +;; some simple surface patterns it may encounter. (let [optimize-closure (fn [optimize closure] (&/|map (fn [capture] (|let [[_name _analysis] capture] @@ -342,6 +528,9 @@ (&a/$bool value) (&/T [meta ($bool value)]) + (&a/$nat value) + (&/T [meta ($nat value)]) + (&a/$int value) (&/T [meta ($int value)]) @@ -365,9 +554,34 @@ (&a/$case value branches) (|case branches + ;; The pattern for a let-expression is a single branch, + ;; tying the value to a register. (&/$Cons [(&a-case/$StoreTestAC _register) _body] (&/$Nil)) (&/T [meta ($let (pass-0 value) _register (pass-0 _body))]) + (&/$Cons [(&a-case/$BoolTestAC false) _else] + (&/$Cons [(&a-case/$BoolTestAC true) _then] + (&/$Nil))) + (&/T [meta ($if (pass-0 value) (pass-0 _then) (pass-0 _else))]) + + ;; The pattern for a record-get is a single branch, with a + ;; tuple pattern and a body corresponding to a + ;; local-variable extracted from the tuple. + (&/$Cons [(&a-case/$TupleTestAC _sub-tests) [_ (&a/$var (&/$Local _member-idx))]] (&/$Nil)) + (|let [_path (record-read-path _sub-tests _member-idx)] + (if (&/|empty? _path) + ;; If the path is empty, that means it was a + ;; false-positive and normal PM optimization should be + ;; done instead. + (&/T [meta ($case (pass-0 value) + (optimize-pm (&/|map (fn [branch] + (|let [[_pattern _body] branch] + (&/T [_pattern (pass-0 _body)]))) + branches)))]) + ;; Otherwise, we've got ourselves a record-get expression. + (&/T [meta ($record-get (pass-0 value) _path)]))) + + ;; If no special patterns are found, just do normal PM optimization. _ (&/T [meta ($case (pass-0 value) (optimize-pm (&/|map (fn [branch] @@ -377,9 +591,14 @@ (&a/$lambda scope captured body) (|case (pass-0 body) + ;; If the body of a function is another function, that means + ;; no work was done in-between and both layers can be folded + ;; into one. [_ ($function _arity _scope _captured _body)] (&/T [meta ($function (inc _arity) scope (optimize-closure pass-0 captured) (shift-function-body scope _scope true _body))]) + ;; Otherwise, they're nothing to be done and we've got a + ;; 1-arity function. =body (&/T [meta ($function 1 scope (optimize-closure pass-0 captured) =body)])) diff --git a/src/lux/parser.clj b/src/lux/parser.clj index 5b0bfce57..d5b4a54cd 100644 --- a/src/lux/parser.clj +++ b/src/lux/parser.clj @@ -76,6 +76,9 @@ (&lexer/$Bool ?value) (return (&/|list (&/T [meta (&/$BoolS (Boolean/parseBoolean ?value))]))) + (&lexer/$Nat ?value) + (return (&/|list (&/T [meta (&/$NatS (Long/parseUnsignedLong ?value))]))) + (&lexer/$Int ?value) (return (&/|list (&/T [meta (&/$IntS (Long/parseLong ?value))]))) diff --git a/src/lux/type.clj b/src/lux/type.clj index e79cfe46d..a198fabba 100644 --- a/src/lux/type.clj +++ b/src/lux/type.clj @@ -28,6 +28,7 @@ (def empty-env &/$Nil) (def Bool (&/$NamedT (&/T ["lux" "Bool"]) (&/$HostT "java.lang.Boolean" &/$Nil))) +(def Nat (&/$NamedT (&/T ["lux" "Nat"]) (&/$HostT "#Nat" &/$Nil))) (def Int (&/$NamedT (&/T ["lux" "Int"]) (&/$HostT "java.lang.Long" &/$Nil))) (def Real (&/$NamedT (&/T ["lux" "Real"]) (&/$HostT "java.lang.Double" &/$Nil))) (def Char (&/$NamedT (&/T ["lux" "Char"]) (&/$HostT "java.lang.Character" &/$Nil))) @@ -91,13 +92,13 @@ TypePair (&/$SumT ;; BoundT - Int + Nat (&/$SumT ;; VarT - Int + Nat (&/$SumT ;; ExT - Int + Nat (&/$SumT ;; UnivQ (&/$ProdT TypeList Type) @@ -120,25 +121,28 @@ ;; BoolM Bool (&/$SumT - ;; IntM - Int + ;; NatM + Nat (&/$SumT - ;; RealM - Real + ;; IntM + Int (&/$SumT - ;; CharM - Char + ;; RealM + Real (&/$SumT - ;; TextM - Text + ;; CharM + Char (&/$SumT - ;; IdentM - Ident + ;; TextM + Text (&/$SumT - ;; ListM - (&/$AppT List DefMetaValue) - ;; DictM - (&/$AppT List (&/$ProdT Text DefMetaValue))))))))) + ;; IdentM + Ident + (&/$SumT + ;; ListM + (&/$AppT List DefMetaValue) + ;; DictM + (&/$AppT List (&/$ProdT Text DefMetaValue)))))))))) ) &/$VoidT)))) diff --git a/src/lux/type/host.clj b/src/lux/type/host.clj index 1f451bbdd..75825514e 100644 --- a/src/lux/type/host.clj +++ b/src/lux/type/host.clj @@ -16,6 +16,7 @@ ;; [Exports] (def array-data-tag "#Array") (def null-data-tag "#Null") +(def nat-data-tag "#Nat") ;; [Utils] (defn ^:private trace-lineage* [^Class super-class ^Class sub-class] @@ -267,7 +268,15 @@ (and (= array-data-tag e!name) (not= array-data-tag a!name)) (check-error "" (&/$HostT e!name e!params) (&/$HostT a!name a!params)) - + + (and (= nat-data-tag e!name) + (= nat-data-tag a!name)) + (return fixpoints) + + (or (= nat-data-tag e!name) + (= nat-data-tag a!name)) + (check-error "" (&/$HostT e!name e!params) (&/$HostT a!name a!params)) + :else (let [e!name (as-obj e!name) a!name (as-obj a!name)] |