From 74d3233f7d7984ebcef0d4e6778f0596e867de6c Mon Sep 17 00:00:00 2001 From: Eduardo Julian Date: Sat, 20 Dec 2014 23:25:40 -0400 Subject: * Java interop is almost finished. % _. and _.. syntax has been replaced with :: --- src/lang.clj | 1 - src/lang/analyser.clj | 110 +++++++++++++++++++++++++++++++++++++++++++------- src/lang/compiler.clj | 64 ++++++++++++++++++++++------- src/lang/parser.clj | 13 ++---- src/lang/type.clj | 64 ++++++++++++++++++++++++++++- src/lang/util.clj | 5 +++ test2.lang | 4 +- 7 files changed, 220 insertions(+), 41 deletions(-) diff --git a/src/lang.clj b/src/lang.clj index 84535356e..f4ef4a9fc 100644 --- a/src/lang.clj +++ b/src/lang.clj @@ -11,7 +11,6 @@ (.write stream data))) (comment - ;; TODO: Add Java-interop. ;; TODO: Allow loading classes/modules at runtime. ;; TODO: Add macros. ;; TODO: Re-implement compiler in language. diff --git a/src/lang/analyser.clj b/src/lang/analyser.clj index 4b1b95836..4436b0b61 100644 --- a/src/lang/analyser.clj +++ b/src/lang/analyser.clj @@ -131,7 +131,7 @@ (defn ^:private import-class [long-name short-name] (fn [state] - (let [=class (annotated [::class long-name] ::&type/nothing)] + (let [=class (annotated [::class long-name] [::&type/object long-name []])] [::&util/ok [(update-in state [:imports] merge {long-name =class, short-name =class}) nil]]))) @@ -246,22 +246,105 @@ ] (return =ident))) -(defanalyser analyse-static-access +(defanalyser analyse-access [::&parser/static-access ?target ?member] - (exec [=target (resolve ?target) - ;; :let [_ (prn '=target ?target (:form =target))] - ] + (exec [=target (resolve ?target)] (match (:form =target) [::class ?class] (return (annotated [::static-access ?class ?member] ::&type/nothing))))) -(defanalyser analyse-dynamic-access - [::&parser/dynamic-access ?object ?member] - (exec [=object (analyse-form* ?object)] - (match ?member - [::&parser/fn-call [::&parser/ident ?method] ?args] - (exec [=args (map-m analyse-form* ?args)] - (return (annotated [::dynamic-access =object [?method =args]] ::&type/nothing)))))) +(defn extract-ident [ident] + (match ident + [::&parser/ident ?ident] + (return ?ident) + + _ + (fail ""))) + +(defn extract-class [x] + (match x + [::class ?class] + (return ?class) + + _ + (fail ""))) + +(defn class-type [x] + (match x + [::&type/object ?class []] + (return ?class) + + _ + (fail ""))) + +(defn lookup-field [mode target field] + ;; (prn 'lookup-field mode target field) + (if-let [[[owner type]] (seq (for [=field (.getFields (Class/forName target)) + ;; :let [_ (prn target (.getName =field) (if (java.lang.reflect.Modifier/isStatic (.getModifiers =field)) + ;; :static + ;; :dynamic))] + :when (and (= field (.getName =field)) + (case mode + :static (java.lang.reflect.Modifier/isStatic (.getModifiers =field)) + :dynamic (not (java.lang.reflect.Modifier/isStatic (.getModifiers =field)))))] + [(.getDeclaringClass =field) (.getType =field)]))] + (exec [=type (&type/class->type type)] + (return [(.getName owner) =type])) + (fail (str "Field does not exist: " target field mode)))) + +(defn lookup-method [mode target method args] + ;; (prn 'lookup-method mode target method args) + (if-let [methods (seq (for [=method (.getMethods (Class/forName target)) + ;; :let [_ (prn target (.getName =method) (if (java.lang.reflect.Modifier/isStatic (.getModifiers =method)) + ;; :static + ;; :dynamic))] + :when (and (= method (.getName =method)) + (case mode + :static (java.lang.reflect.Modifier/isStatic (.getModifiers =method)) + :dynamic (not (java.lang.reflect.Modifier/isStatic (.getModifiers =method)))))] + [(.getDeclaringClass =method) =method]))] + (map-m (fn [[owner method]] + (exec [=method (&type/method->type method)] + (return [(.getName owner) =method]))) + methods) + (fail (str "Method does not exist: " target method mode)))) + +(defanalyser analyse-access + [::&parser/access ?object ?member] + (match ?member + [::&parser/ident ?field] ;; Field + (try-all-m [(exec [?target (extract-ident ?object) + =target (resolve ?target) + ?class (extract-class (:form =target)) + [=owner =type] (lookup-field :static ?class ?field) + ;; :let [_ (prn '=type =type)] + ] + (return (annotated [::static-field =owner ?field] =type))) + (exec [=target (analyse-form* ?object) + ?class (class-type (:type =target)) + [=owner =type] (lookup-field :dynamic ?class ?field) + ;; :let [_ (prn '=type =type)] + ] + (return (annotated [::dynamic-field =target =owner ?field] =type)))]) + [::&parser/fn-call [::&parser/ident ?method] ?args] ;; Method + (exec [=args (map-m analyse-form* ?args)] + (try-all-m [(exec [?target (extract-ident ?object) + =target (resolve ?target) + ?class (extract-class (:form =target)) + =methods (lookup-method :static ?class ?method (map :type =args)) + ;; :let [_ (prn '=methods =methods)] + [=owner =method] (within :types (&type/pick-matches =methods (map :type =args))) + ;; :let [_ (prn '=method =owner ?method =method)] + ] + (return (annotated [::static-method =owner ?method =method =args] (&type/return-type =method)))) + (exec [=target (analyse-form* ?object) + ?class (class-type (:type =target)) + =methods (lookup-method :dynamic ?class ?method (map :type =args)) + ;; :let [_ (prn '=methods =methods)] + [=owner =method] (within :types (&type/pick-matches =methods (map :type =args))) + ;; :let [_ (prn '=method =owner ?method =method)] + ] + (return (annotated [::dynamic-method =target =owner ?method =method =args] (&type/return-type =method))))])))) (defanalyser analyse-fn-call [::&parser/fn-call ?fn ?args] @@ -415,8 +498,7 @@ analyse-tuple analyse-lambda analyse-ident - analyse-static-access - analyse-dynamic-access + analyse-access analyse-fn-call analyse-if analyse-do diff --git a/src/lang/compiler.clj b/src/lang/compiler.clj index 83aa14252..18343db1d 100644 --- a/src/lang/compiler.clj +++ b/src/lang/compiler.clj @@ -67,6 +67,14 @@ [::&type/function ?args ?return] (->java-sig [::&type/object "test2/Function" []]))) +(defn ^:private method->sig [method] + (match method + [::&type/function ?args ?return] + (str "(" (apply str (map ->java-sig ?args)) ")" + (if (= ::&type/nothing ?return) + "V" + (->java-sig ?return))))) + ;; [Utils/Compilers] (defcompiler ^:private compile-literal [::&analyser/literal ?literal] @@ -180,19 +188,45 @@ (.visitMethodInsn *writer* Opcodes/INVOKEINTERFACE "test2/Function" "apply" apply-signature)))) ))) -(defcompiler ^:private compile-static-access - [::&analyser/static-access ?class ?member] - (doto *writer* - (.visitFieldInsn Opcodes/GETSTATIC (->class ?class) ?member (->type-signature "java.io.PrintStream")))) +(defcompiler ^:private compile-static-field + [::&analyser/static-field ?owner ?field] + (do ;; (prn 'compile-static-field ?owner ?field) + ;; (assert false) + (doto *writer* + (.visitFieldInsn Opcodes/GETSTATIC (->class ?owner) ?field (->java-sig *type*))) + )) -(defcompiler ^:private compile-dynamic-access - [::&analyser/dynamic-access ?object [?method ?args]] - (do (compile-form (assoc *state* :form ?object)) - (doseq [arg ?args] - (compile-form (assoc *state* :form arg))) - (doto *writer* - (.visitMethodInsn Opcodes/INVOKEVIRTUAL (->class "java.io.PrintStream") ?method "(Ljava/lang/Object;)V") - (.visitInsn Opcodes/ACONST_NULL)))) +(defcompiler ^:private compile-dynamic-field + [::&analyser/dynamic-field ?target ?owner ?field] + (do ;; (prn 'compile-static-field ?owner ?field) + ;; (assert false) + (compile-form (assoc *state* :form ?target)) + (doto *writer* + (.visitFieldInsn Opcodes/GETFIELD (->class ?owner) ?field (->java-sig *type*))) + )) + +(defcompiler ^:private compile-static-method + [::&analyser/static-method ?owner ?method-name ?method-type ?args] + (do ;; (prn 'compile-dynamic-access ?target ?owner ?method-name ?method-type ?args) + ;; (assert false) + (do (doseq [arg ?args] + (compile-form (assoc *state* :form arg))) + (doto *writer* + (.visitMethodInsn Opcodes/INVOKESTATIC (->class ?owner) ?method-name (method->sig ?method-type)) + (.visitInsn Opcodes/ACONST_NULL))) + )) + +(defcompiler ^:private compile-dynamic-method + [::&analyser/dynamic-method ?target ?owner ?method-name ?method-type ?args] + (do ;; (prn 'compile-dynamic-access ?target ?owner ?method-name ?method-type ?args) + ;; (assert false) + (do (compile-form (assoc *state* :form ?target)) + (doseq [arg ?args] + (compile-form (assoc *state* :form arg))) + (doto *writer* + (.visitMethodInsn Opcodes/INVOKEVIRTUAL (->class ?owner) ?method-name (method->sig ?method-type)) + (.visitInsn Opcodes/ACONST_NULL))) + )) (defcompiler ^:private compile-if [::&analyser/if ?test ?then ?else] @@ -595,8 +629,10 @@ compile-captured compile-global compile-call - compile-static-access - compile-dynamic-access + compile-static-field + compile-dynamic-field + compile-static-method + compile-dynamic-method compile-if compile-do compile-case diff --git a/src/lang/parser.clj b/src/lang/parser.clj index ea3f518f5..376e376d6 100644 --- a/src/lang/parser.clj +++ b/src/lang/parser.clj @@ -158,15 +158,11 @@ =record (apply-m parse-form (list ?record))] (return [::set ?tag =value =record]))) -(defparser ^:private parse-static-access - [::&lexer/list ([[::&lexer/ident "_.."] [::&lexer/ident ?class] [::&lexer/ident ?member]] :seq)] - (return [::static-access ?class ?member])) - -(defparser ^:private parse-dynamic-access - [::&lexer/list ([[::&lexer/ident "_."] ?object ?call] :seq)] +(defparser ^:private parse-access + [::&lexer/list ([[::&lexer/ident "::"] ?object ?call] :seq)] (exec [=object (apply-m parse-form (list ?object)) =call (apply-m parse-form (list ?call))] - (return [::dynamic-access =object =call]))) + (return [::access =object =call]))) (defparser ^:private parse-string [::&lexer/string ?string] @@ -199,8 +195,7 @@ parse-get parse-set parse-remove - parse-static-access - parse-dynamic-access + parse-access parse-defclass parse-definterface parse-import diff --git a/src/lang/type.clj b/src/lang/type.clj index 465f6e9fc..cfb404a21 100644 --- a/src/lang/type.clj +++ b/src/lang/type.clj @@ -3,7 +3,7 @@ (:require [clojure.core.match :refer [match]] [lang.util :as &util :refer [exec return* return fail fail* repeat-m try-m try-all-m map-m - apply-m]])) + apply-m assert!]])) ;; [Util] (def ^:private success (return nil)) @@ -39,6 +39,7 @@ (return [=function =args =return]))) (defn solve [expected actual] + ;; (prn 'solve expected actual) (match [expected actual] [::any _] success @@ -64,10 +65,35 @@ _ (update ?id =top actual)] success)])) + [[::primitive ?prim] _] + (let [as-obj (case ?prim + "boolean" [:lang.type/object "java.lang.Boolean" []] + "int" [:lang.type/object "java.lang.Integer" []] + "long" [:lang.type/object "java.lang.Long" []] + "char" [:lang.type/object "java.lang.Character" []] + "float" [:lang.type/object "java.lang.Float" []] + "double" [:lang.type/object "java.lang.Double" []])] + (solve as-obj actual)) + + [[::object ?eclass []] [::object ?aclass []]] + (if (.isAssignableFrom (Class/forName ?eclass) (Class/forName ?aclass)) + success + (fail (str "Can't solve types: " (pr-str expected actual)))) + [_ _] (fail (str "Can't solve types: " (pr-str expected actual))) )) +(defn pick-matches [methods args] + (if (empty? methods) + (fail "No matches.") + (try-all-m [(match (-> methods first second) + [::function ?args ?return] + (exec [_ (assert! (= (count ?args) (count args)) "Args-size doesn't match.") + _ (map-m (fn [[e a]] (solve e a)) (map vector ?args args))] + (return (first methods)))) + (pick-matches (rest methods) args)]))) + (defn clean [type] (match type [::var ?id] @@ -84,3 +110,39 @@ _ (return type))) + +;; Java Reflection +(defn class->type [class] + (if-let [[_ base arr-level] (re-find #"^([^\[]+)(\[\])*$" + (str (if-let [pkg (.getPackage class)] + (str (.getName pkg) ".") + "") + (.getSimpleName class)))] + (if (= "void" base) + (return ::nothing) + (let [base* (case base + ("boolean" "byte" "short" "int" "long" "float" "double" "char") + [::primitive base] + ;; else + [::object base []])] + (if arr-level + (return (reduce (fn [inner _] + [::array inner]) + base* + (range (/ (count arr-level) 2.0)))) + (return base*))) + + ))) + +(defn method->type [method] + (exec [=args (map-m class->type (seq (.getParameterTypes method))) + =return (class->type (.getReturnType method))] + (return [::function (vec =args) =return]))) + +(defn return-type [func] + (match func + [::function _ ?return] + (return ?return) + + _ + (fail (str "Type is not a function: " (pr-str func))))) diff --git a/src/lang/util.clj b/src/lang/util.clj index ec93ac27f..cdfa8555d 100644 --- a/src/lang/util.clj +++ b/src/lang/util.clj @@ -101,6 +101,11 @@ [::failure _] output)))) +(defn assert! [test message] + (if test + (return nil) + (fail message))) + (defn comp-m [f-m g-m] (exec [temp g-m] (f-m temp))) diff --git a/test2.lang b/test2.lang index 758f01720..90db0230b 100644 --- a/test2.lang +++ b/test2.lang @@ -24,8 +24,8 @@ (if true (let f (lambda [x] (lambda [y] (x y))) (let g (lambda [x] x) - (_. (_.. System out) (println (f g "WE'VE GOT CLOSURES!"))))) - (_. (_.. System out) (println "FALSE")))) + (:: (:: System out) (println (f g "WE'VE GOT CLOSURES!"))))) + (:: (:: System out) (println "FALSE")))) ## All of these work :D #( (let output "IT WORKS!" -- cgit v1.2.3