From 648110a554a13e1caaf846a60c85cccadcda6e0d Mon Sep 17 00:00:00 2001 From: Eduardo Julian Date: Sat, 3 Jan 2015 01:48:08 -0400 Subject: The language now supports macros. --- src/lang.clj | 181 ++++----------------------------------------- src/lang/analyser.clj | 169 +++++++++++++++++++++++++++++++++++++++--- src/lang/compiler.clj | 199 ++++++++++++++++++++++++++++---------------------- src/lang/lexer.clj | 2 +- src/lang/parser.clj | 7 ++ src/lang/util.clj | 25 +++++++ 6 files changed, 316 insertions(+), 267 deletions(-) (limited to 'src') diff --git a/src/lang.clj b/src/lang.clj index 5e4316db4..0777812b7 100644 --- a/src/lang.clj +++ b/src/lang.clj @@ -16,7 +16,6 @@ ;; TODO: Adding metadata to global vars. ;; TODO: Add records. ;; TODO: throw, try, catch, finally - ;; TODO: Finish implementing pattern matching. ;; TODO: Tuple8 and Tuple8X (for arbitrary-size tuples). ;; TODO: Add extra arities (apply2, apply3, ..., apply16) ;; TODO: When doing partial application, skip "apply" and just call constructor appropiatedly. @@ -32,176 +31,24 @@ ;; _ (prn 'tokens tokens) syntax (&parser/parse tokens) ;; _ (prn 'syntax syntax) - ann-syntax (&analyser/analyse "test2" syntax) + ;; ann-syntax (&analyser/analyse "test2" syntax) ;; _ (prn 'ann-syntax ann-syntax) - class-data (&compiler/compile "test2" ann-syntax) + ;; class-data (&compiler/compile "test2" ann-syntax) + class-data (&compiler/compile "test2" syntax) ;; _ (prn 'class-data class-data) ] - (with-open [stream (java.io.BufferedOutputStream. (java.io.FileOutputStream. "test2.class"))] - (.write stream class-data))) - - ;; jar cvf test2.jar *.class test2 && java -cp "test2.jar" test2 - ) - -(comment - ;; (let [branches '([::&parser/case-branch [::&parser/variant "Cons" ([::&parser/variant "Symbol" ([::&parser/string "~"])] [::&parser/variant "Cons" ([::&parser/ident "unquoted"] [::&parser/variant "Nil" ()])])] [::&parser/variant "Cons" ([::&parser/ident "unquoted"] [::&parser/fn-call [::&parser/ident "template"] ([::&parser/ident "tail"])])]] - ;; [::&parser/case-branch [::&parser/variant "Cons" ([::&parser/variant "Symbol" ([::&parser/string "~@"])] [::&parser/variant "Cons" ([::&parser/ident "spliced"] [::&parser/variant "Nil" ()])])] [::&parser/variant "Cons" ([::&parser/variant "Symbol" ([::&parser/string "++"])] [::&parser/variant "Cons" ([::&parser/ident "spliced"] [::&parser/fn-call [::&parser/ident "template"] ([::&parser/ident "tail"])])])]] - ;; [::&parser/case-branch [::&parser/ident "_"] [::&parser/variant "Cons" ([::&parser/ident "head"] [::&parser/fn-call [::&parser/ident "template"] ([::&parser/ident "tail"])])]]) - ;; ;; Step 1: Get all vars - ;; get-vars (fn get-vars [pattern] - ;; (clojure.core.match/match pattern - ;; [::&parser/ident ?name] - ;; (list ?name) - - ;; [::&parser/variant ?tag ?members] - ;; (mapcat get-vars ?members) - - ;; [::&parser/string ?text] - ;; '())) - ;; vars+body (for [branch branches] - ;; (clojure.core.match/match branch - ;; [::&parser/case-branch ?pattern ?body] - ;; [(get-vars ?pattern) ?body])) - ;; ;; _ (prn 'vars+body vars+body) - ;; max-registers (reduce max 0 (map (comp count first) vars+body)) - ;; ;; _ (prn 'max-registers max-registers) - ;; ;; Step 2: Analyse bodies - ;; ;; all-analysis (map (fn [[vars body]] - ;; ;; (reduce #(with-local %2 [::&type/object "java.lang.Object" []] %1) - ;; ;; (analyse-form* body) - ;; ;; (reverse vars))) - ;; ;; vars+body) - ;; ;; Step 3: Extract bodies - ;; [_ branch-mappings branches*] (reduce (fn [[$link links branches*] branch] - ;; (clojure.core.match/match branch - ;; [::&parser/case-branch ?pattern ?body] - ;; [(inc $link) (assoc links $link ?body) (conj branches* [::&parser/case-branch ?pattern $link])])) - ;; [0 {} []] - ;; branches) - ;; ;; Step 4: Pattens -> Instructions - ;; ;; ->instructions (fn ->instructions [locals pattern] - ;; ;; (clojure.core.match/match pattern - ;; ;; [::&parser/variant ?tag ?members] - ;; ;; [::pm-variant ?tag (map (partial ->instructions locals) ?members)] - - ;; ;; [::&parser/ident ?name] - ;; ;; [::pm-local (get locals ?name)] - - ;; ;; [::&parser/string ?text] - ;; ;; [::pm-text ?text] - ;; ;; )) - ;; ;; $scope 0 ;; scope-id - ;; ;; $local 11 ;; next-local-idx - ;; ;; branches** (for [[branch branch-vars] (map vector branches* (map first vars+body)) - ;; ;; :let [[_ locals] (reduce (fn [[$local =locals] $var] - ;; ;; [(inc $local) (assoc =locals $var [::local $scope $local])]) - ;; ;; [$local {}] branch-vars)]] - ;; ;; (clojure.core.match/match branch - ;; ;; [::&parser/case-branch ?pattern ?body] - ;; ;; [(->instructions locals ?pattern) ?body])) - ;; ;; _ (prn branches**) - ;; ;; Step 5: Re-structure branching - ;; ] - ;; ;; [branch-mappings branches**] - ;; branches*) - - - + ;; (with-open [stream (java.io.BufferedOutputStream. (java.io.FileOutputStream. "test2.class"))] + ;; (.write stream class-data)) + ) + (Class/forName "test2.Variant") - ;; (let [data '([[:lang/pm-variant "Cons" ([:lang/pm-variant "Symbol" ([:lang/pm-text "~"])] [:lang/pm-variant "Cons" ([:lang/pm-local [:lang/local 0 11]] [:lang/pm-variant "Nil" ()])])] 0] - ;; [[:lang/pm-variant "Cons" ([:lang/pm-variant "Symbol" ([:lang/pm-text "~@"])] [:lang/pm-variant "Cons" ([:lang/pm-local [:lang/local 0 11]] [:lang/pm-variant "Nil" ()])])] 1] - ;; [[:lang/pm-local [:lang/local 0 11]] 2]) - ;; classify-outer (fn [struct [branch $body]] - ;; (clojure.core.match/match branch - ;; [::pm-variant ?tag ?members] - ;; (update-in struct [:cases ?tag] conj {:members ?members - ;; :body $body}) - - ;; [::pm-text ?text] - ;; (update-in struct [:tests] conj {:test [::text ?text] - ;; :body $body}) - - ;; [::pm-local ?binding] - ;; (assoc struct :default {:storage ?binding - ;; :body $body}))) - ;; outer-classification (reduce classify-outer - ;; {:cases {} - ;; :tests '() - ;; :default nil} - ;; data) - ;; full-classifier (fn full-classifier [global] - ;; (prn 'full-classifier global) - ;; (let [subcases (:cases global)] - ;; (if (empty? subcases) - ;; global - ;; (let [crossed (sort (fn [x1 x2] (> (-> x1 second :cases count) (-> x2 second :cases count))) - ;; (for [[tag subs] subcases - ;; :let [_ (prn 'subcases tag subs)] - ;; :let [parts (for [cross (apply map list (map :members subs)) - ;; :let [_ (prn 'cross tag cross)] - ;; ;; :let [_ (prn '(map :body subs) (map :body subs))] - ;; ;; :let [_ (prn (class cross) (count cross) - ;; ;; (class (map :body subs)) (count (map :body subs)))] - ;; :let [cross+ (map vector cross (map :body subs))] - ;; ;; :let [_ (prn 'cross+ tag (class cross+) (count cross+))] - ;; ;; :let [_ (prn 'cross+ tag cross+)] - ;; :let [cross++ (reduce classify-outer - ;; {:cases {} - ;; :tests '() - ;; :default nil} - ;; cross+)] - ;; ;; :let [_ (prn 'cross++ tag cross++)] - ;; ] - ;; cross++)] - ;; :let [_ (prn 'parts parts)]] - ;; [tag parts])) - - ;; ] - ;; (assoc global :cases (reduce (fn [tree [tag subcases]] - ;; (update-in tree [tag] #(conj (or % []) (full-classifier subcases)))) - ;; {} - ;; crossed))))))] - ;; (full-classifier outer-classification)) + ;; jar cvf test2.jar *.class test2 && java -cp "test2.jar" test2 ) -(comment - [{2 [:lang.parser/variant "Cons" ([:lang.parser/ident "head"] [:lang.parser/fn-call [:lang.parser/ident "template"] ([:lang.parser/ident "tail"])])], - 1 [:lang.parser/variant "Cons" ([:lang.parser/variant "Symbol" ([:lang.parser/string "++"])] [:lang.parser/variant "Cons" ([:lang.parser/ident "spliced"] [:lang.parser/fn-call [:lang.parser/ident "template"] ([:lang.parser/ident "tail"])])])], - 0 [:lang.parser/variant "Cons" ([:lang.parser/ident "unquoted"] [:lang.parser/fn-call [:lang.parser/ident "template"] ([:lang.parser/ident "tail"])])]} - {:type :lang/adt*, - :patterns {"Cons" ({:type :lang/adt*, - :patterns {"Symbol" ({:type :lang/text-tests, - :patterns {"~@" #{1}, - "~" #{0}}, - :defaults [], - :branches #{0 1}})}, - :default nil, - :branches #{0 1}} - {:type :lang/adt*, - :patterns {"Cons" ({:type :lang/defaults, - :stores {[:lang/local 0 11] #{0 1}}, - :branches #{0 1}} - {:type :lang/adt*, - :patterns {"Nil" ()}, - :default nil, - :branches #{0 1}})}, - :default nil, - :branches #{0 1}})}, - :default [:lang/default [:lang/local 0 11] 2], - :branches #{0 1 2}}] - - (let [data '([[:lang/pm-variant "Cons" ([:lang/pm-variant "Symbol" ([:lang/pm-text "~"])] [:lang/pm-variant "Cons" ([:lang/pm-local [:lang/local 0 11]] [:lang/pm-variant "Nil" ()])])] 0] - [[:lang/pm-variant "Cons" ([:lang/pm-variant "Symbol" ([:lang/pm-text "~@"])] [:lang/pm-variant "Cons" ([:lang/pm-local [:lang/local 0 11]] [:lang/pm-variant "Nil" ()])])] 1] - [[:lang/pm-local [:lang/local 0 11]] 2]) - ] - (generate-branches data)) - - ;; (def (workday? d) - ;; (case d - ;; (or [#Monday #Tuesday #Wednesday #Thursday #Friday] - ;; true) - ;; (or [#Saturday #Sunday] - ;; false))) - - ) +;; (def (workday? d) +;; (case d +;; (or [#Monday #Tuesday #Wednesday #Thursday #Friday] +;; true) +;; (or [#Saturday #Sunday] +;; false))) diff --git a/src/lang/analyser.clj b/src/lang/analyser.clj index 21117a7b7..1e2c684bb 100644 --- a/src/lang/analyser.clj +++ b/src/lang/analyser.clj @@ -5,18 +5,22 @@ [clojure.core.match :refer [match]] (lang [util :as &util :refer [exec return* return fail fail* repeat-m try-m try-all-m map-m reduce-m - apply-m within]] + apply-m within + normalize-ident + loader]] [parser :as &parser] [type :as &type]))) -(declare analyse-form) +(declare analyse-form + ->tokens + tokens->clojure) ;; [Util] (defn ^:private annotated [form type] {:form form :type type}) -(defn ^:private fresh-env [id] +(defn fresh-env [id] {:id id :counter 0 :mappings {} @@ -33,6 +37,13 @@ (assoc-in [:defs-env name] (annotated [::global (:name state) name] (:type desc)))) nil]])) +(defn ^:private is-macro? [name] + (fn [state] + (prn 'is-macro? (nth name 1) + (get-in state [:defs (:name state) (nth name 1) :mode]) + (= (get-in state [:defs (:name state) (nth name 1) :mode]) ::macro)) + [::&util/ok [state (= (get-in state [:defs (:name state) (nth name 1) :mode]) ::macro)]])) + (def ^:private next-local-idx (fn [state] [::&util/ok [state (-> state :env first :counter)]])) @@ -45,7 +56,21 @@ (fn [state] [::&util/ok [state (-> state :env first)]])) -(defn ^:private with-scope [scope body] +(defn ^:private in-scope? [scope] + (fn [state] + (match scope + [::&parser/ident ?macro-name] + (do ;; (prn 'in-scope? + ;; ?macro-name + ;; (get-in state [:lambda-scope 0]) + ;; (some (partial = ?macro-name) (get-in state [:lambda-scope 0]))) + [::&util/ok [state (some (partial = ?macro-name) (get-in state [:lambda-scope 0]))]]) + + _ + [::&util/ok [state false]]) + )) + +(defn with-scope [scope body] (fn [state] (let [=return (body (-> state (update-in [:lambda-scope 0] conj scope) @@ -237,8 +262,9 @@ [::&util/ok [?state ?value]] [::&util/ok [(assoc ?state :forms old-forms) ?value]] - _ - =return)))) + [::&util/failure ?message] + (do (prn 'analyse-form* ?message) + [::&util/failure ?message]))))) (do-template [ ] (defanalyser @@ -380,15 +406,118 @@ ] (return (annotated [::dynamic-method =target =owner ?method =method =args] (&type/return-type =method))))])))) +(defn ->token [x] + (prn '->token x) + (let [variant (.newInstance (.loadClass loader "test2.Variant"))] + (match x + [::&parser/string ?text] + (doto variant + (-> .-tag (set! "Text")) + (-> .-value (set! (doto (.newInstance (.loadClass loader "test2.Tuple1")) + (-> .-_0 (set! ?text)))))) + [::&parser/ident ?ident] + (doto variant + (-> .-tag (set! "Ident")) + (-> .-value (set! (doto (.newInstance (.loadClass loader "test2.Tuple1")) + (-> .-_0 (set! ?ident)))))) + [::&parser/fn-call ?fn ?args] + (doto variant + (-> .-tag (set! "Form")) + (-> .-value (set! (doto (.newInstance (.loadClass loader "test2.Tuple1")) + (-> .-_0 (set! (->tokens (cons ?fn ?args)))))) + )) + ))) + +(defn ->tokens [xs] + (let [variant (.loadClass loader "test2.Variant") + tuple2 (.loadClass loader "test2.Tuple2")] + (reduce (fn [tail x] + ;; (prn 'tail (.-tag tail) 'x x) + (doto (.newInstance variant) + (-> .-tag (set! "Cons")) + (-> .-value (set! (doto (.newInstance tuple2) + (-> .-_0 (set! (->token x))) + (-> .-_1 (set! tail)) + ;; (-> prn) + ))) + ;; (-> prn) + )) + (doto (.newInstance variant) + (-> .-tag (set! "Nil")) + (-> .-value (set! (.newInstance (.loadClass loader "test2.Tuple0"))))) + (reverse xs)))) + +(defn ->clojure-token [x] + (prn '->clojure-token x (.-tag x)) + (case (.-tag x) + "Text" [::&parser/string (-> x .-value .-_0 (doto (-> string? assert)))] + "Ident" [::&parser/ident (-> x .-value .-_0 (doto (-> string? assert)))] + "Form" (let [[?fn & ?args] (-> x .-value .-_0 tokens->clojure)] + [::&parser/fn-call ?fn ?args]) + "Quote" [::&parser/quote (-> x .-value .-_0 ->clojure-token)])) + +(defn tokens->clojure [xs] + (prn 'tokens->clojure xs (.-tag xs)) + (case (.-tag xs) + "Nil" '() + "Cons" (let [tuple2 (.-value xs)] + (cons (->clojure-token (.-_0 tuple2)) + (tokens->clojure (.-_1 tuple2)))) + )) + +;; (defn ->clojure-tokens [xs] +;; (case (.-tag xs) +;; "Cons" (let [tuple2 (.-value xs)] +;; (cons (->clojure-token (.-_0 tuple2)) (->clojure-tokens (.-_1 tuple2)))) +;; "Nil" '())) + +(comment + (-> (->token [::&parser/string "YOLO"]) + .-value + .-_0) + + (-> (->tokens (list [::&parser/string "YOLO"])) + ;; .-tag + .-value + .-_1 + .-tag + ) + + (let [_ (prn 'loader loader) + macro (-> loader (.loadClass "test2$_QUOTE_") .newInstance) + tokens (->tokens (list [::&parser/string "YOLO"]))] + (prn macro) + (prn tokens) + (prn (.apply macro tokens)) + (prn (->clojure-token (.apply macro tokens))) + ) + + + ) + (defanalyser analyse-fn-call [::&parser/fn-call ?fn ?args] (exec [;; :let [_ (prn 'PRE '?fn ?fn)] + macro? (is-macro? ?fn) + scoped? (in-scope? ?fn) + :let [;; _ (prn 'macro? ?fn macro?) + ;; _ (prn 'scoped? scoped?) + ] =fn (analyse-form* ?fn) ;; :let [_ (prn '=fn =fn)] - =args (map-m analyse-form* ?args) ;; :let [_ (prn '=args =args)] ] - (return (annotated [::call =fn =args] [::&type/object "java.lang.Object" []])))) + (if (and macro? (not scoped?)) + (do ;; (prn "MACRO CALL!" ?fn ?args =fn) + (let [macro (match (:form =fn) + [::global ?module ?name] + (.newInstance (.loadClass loader (str ?module "$" (normalize-ident ?name))))) + output (->clojure-token (.apply macro (->tokens ?args)))] + (prn "MACRO CALL!" macro output) + (analyse-form* output))) + (exec [=args (map-m analyse-form* ?args)] + (return (annotated [::call =fn =args] [::&type/object "java.lang.Object" []])))) + )) (defanalyser analyse-if [::&parser/if ?test ?then ?else] @@ -630,6 +759,20 @@ (return (annotated [::def [?name args] =value] ::&type/nothing)))) )) +(defanalyser analyse-defmacro + [::&parser/defmacro [::&parser/fn-call [::&parser/ident ?name] ([[::&parser/ident ?tokens]] :seq)] ?value] + (exec [[=function =tokens =return] (within :types (&type/fresh-function 1)) + =value (with-scope ?name + (with-scoped-name ?name =function + (with-local ?tokens =tokens + (analyse-form* ?value)))) + =function (within :types (exec [_ (&type/solve =return (:type =value))] + (&type/clean =function))) + _ (define ?name {:mode ::macro + :access ::public + :type =function})] + (return (annotated [::def [?name (list ?tokens)] =value] ::&type/nothing)))) + (defanalyser analyse-lambda [::&parser/lambda ?args ?body] (exec [;; :let [_ (prn 'analyse-lambda ?args ?body)] @@ -661,7 +804,11 @@ (exec [_ (require-module module-name ?alias)] (return (annotated [::require ?file ?alias] ::&type/nothing))))) -(def ^:private analyse-form +(defanalyser analyse-quote + [::&parser/quote ?quoted] + (return (annotated [::quote ?quoted] ::&type/nothing))) + +(def analyse-form (try-all-m [analyse-boolean analyse-int analyse-float @@ -680,8 +827,10 @@ analyse-defclass analyse-definterface analyse-def + analyse-defmacro analyse-import - analyse-require])) + analyse-require + analyse-quote])) ;; [Interface] (defn analyse [module-name tokens] diff --git a/src/lang/compiler.clj b/src/lang/compiler.clj index 2ead6daec..113cb2fed 100644 --- a/src/lang/compiler.clj +++ b/src/lang/compiler.clj @@ -3,7 +3,12 @@ (:require [clojure.string :as string] [clojure.set :as set] [clojure.core.match :refer [match]] - (lang [type :as &type] + (lang [util :as &util :refer [exec return* return fail fail* + repeat-m try-m try-all-m map-m reduce-m + apply-m within + normalize-ident + loader]] + [type :as &type] [lexer :as &lexer] [parser :as &parser] [analyser :as &analyser]) @@ -18,30 +23,22 @@ ;; [Utils/General] (defn ^:private write-file [file data] + (println 'write-file file (alength data)) (with-open [stream (java.io.BufferedOutputStream. (java.io.FileOutputStream. file))] - (.write stream data))) - -(defn ^:private normalize-char [char] - (case char - \* "_ASTER_" - \+ "_PLUS_" - \- "_DASH_" - \/ "_SLASH_" - \_ "_UNDERS_" - \% "_PERCENT_" - \$ "_DOLLAR_" - \! "_BANG_" - \' "_QUOTE_" - \` "_BQUOTE_" - \@ "_AT_" - \^ "_CARET_" - \& "_AMPERS_" - \= "_EQ_" - ;; default - char)) - -(defn ^:private normalize-ident [ident] - (reduce str "" (map normalize-char ident))) + (.write stream data)) + ;; (Thread/sleep 2000) + ) + +(let [;; loader (proxy [ClassLoader] []) + ] + (defn load-class! [name file-name] + (println "Defining..." name "@" file-name ;; (alength bytecode) + ) + ;; (prn 'loader loader) + (.loadClass loader name) + (println "SUCCESFUL LOAD!") + ;; (.defineClass loader name bytecode 0 (alength bytecode)) + )) (def ^:private +variant-class+ "test2.Variant") @@ -290,7 +287,7 @@ +tuple-field-sig+ (->type-signature "java.lang.Object") equals-sig (str "(" (->type-signature "java.lang.Object") ")Z")] (defn compile-decision-tree [writer mappings cleanup-level next-label default-label decision-tree] - (prn 'compile-decision-tree cleanup-level decision-tree) + ;; (prn 'compile-decision-tree cleanup-level decision-tree) (match decision-tree [::test-text ?text $body] (let [$else (new Label)] @@ -326,7 +323,8 @@ (.visitInsn Opcodes/POP) ;; variant (do (let [arity (-> ?subcases first (nth 2) count) tuple-class (str "test2/Tuple" arity) - _ (prn ?tag arity tuple-class)] + ;; _ (prn ?tag arity tuple-class) + ] (when (> arity 0) (doto writer (.visitInsn Opcodes/DUP) ;; variant, variant @@ -355,48 +353,15 @@ ;; variant, tag -> (.visitLabel tag-else-label)) (->> (doseq [[?tag ?subcases] ?cases - :let [_ (.print System/out (prn-str 'COMPILE-PATTERN ?tag ?subcases))] + ;; :let [_ (.print System/out (prn-str 'COMPILE-PATTERN ?tag ?subcases))] :let [tag-else-label (new Label)]]))) (-> (doto (.visitInsn Opcodes/POP)) (->> (dotimes [_ (+ cleanup-level 2)]))) (.visitJumpInsn Opcodes/GOTO default-label))) )) -;; ([:lang.compiler/subcase 0 ([:lang.compiler/test-adt #{0} {"Symbol" ()}] -;; [:lang.compiler/test-adt #{0} {"Nil" ([:lang.compiler/subcase 0 ()])}])] -;; [:lang.compiler/subcase 0 ([:lang.compiler/test-adt #{0} {"Symbol" ()}] -;; [:lang.compiler/store [:lang.analyser/local 0 2] 1])] -;; [:lang.compiler/subcase 0 ([:lang.compiler/store [:lang.analyser/local 0 1] 0] -;; [:lang.compiler/test-adt #{0} {"Nil" ([:lang.compiler/subcase 0 ()])}])] -;; [:lang.compiler/subcase 0 ([:lang.compiler/store [:lang.analyser/local 0 1] 0] -;; [:lang.compiler/store [:lang.analyser/local 0 2] 1])] -;; [:lang.compiler/subcase 1 ([:lang.compiler/test-adt #{1} {"Symbol" ([:lang.compiler/subcase 1 ([:lang.compiler/store [:lang.analyser/local 0 1] 1])])}] -;; [:lang.compiler/test-adt #{1} {"Nil" ()}])] -;; [:lang.compiler/subcase 1 ([:lang.compiler/test-adt #{1} {"Symbol" ([:lang.compiler/subcase 1 ([:lang.compiler/store [:lang.analyser/local 0 1] 1])])}] -;; [:lang.compiler/store [:lang.analyser/local 0 2] 1])] -;; [:lang.compiler/subcase 1 ([:lang.compiler/store [:lang.analyser/local 0 1] 0] -;; [:lang.compiler/test-adt #{0} {"Nil" ([:lang.compiler/subcase 0 ()])}])] -;; [:lang.compiler/subcase 1 ([:lang.compiler/store [:lang.analyser/local 0 1] 0] -;; [:lang.compiler/store [:lang.analyser/local 0 2] 1])]) - -;; ({:type :lang.analyser/adt*, -;; :patterns {"Cons" {:parts ({:type :lang.analyser/adt*, -;; :patterns {"Symbol" {:parts ({:type :lang.analyser/defaults, -;; :stores {[:lang.analyser/local 0 1] #{1}}, -;; :branches #{1}}), -;; :branches #{1}}}, -;; :default [:lang.analyser/default [:lang.analyser/local 0 1] 0], -;; :branches #{0 1}} -;; {:type :lang.analyser/adt*, -;; :patterns {"Nil" {:parts (), :branches #{0}}}, -;; :default [:lang.analyser/default [:lang.analyser/local 0 2] 1], -;; :branches #{0 1}}), -;; :branches #{0 1}}}, -;; :default nil, -;; :branches #{0 1}}) - (defn sequence-parts [branches parts] - (.print System/out (prn-str 'sequence-parts branches parts)) + ;; (.print System/out (prn-str 'sequence-parts branches parts)) (if (empty? parts) '(()) (let [[head & tail] parts @@ -422,14 +387,14 @@ (do ;; (prn '(:default head) (:default head)) ;; (assert (nil? (:default head))) (concat (let [patterns (into {} (for [[?tag ?struct] (:patterns head) - :let [_ (.print System/out (prn-str 'PATTERN ?tag ?struct))] + ;; :let [_ (.print System/out (prn-str 'PATTERN ?tag ?struct))] :let [?parts (:parts ?struct) num-parts (count ?parts) ?supports (:branches ?struct) subcases (for [?body (set/intersection branches ?supports) subseq (sequence-parts #{?body} ?parts) - :let [_ (when (= "Symbol" ?tag) - (.print System/out (prn-str 'counting ?tag num-parts (count subseq) subseq)))] + ;; :let [_ (when (= "Symbol" ?tag) + ;; (.print System/out (prn-str 'counting ?tag num-parts (count subseq) subseq)))] :when (= num-parts (count subseq))] [::subcase ?body subseq])] :when (not (empty? subcases))] @@ -593,8 +558,10 @@ (.visitInsn Opcodes/ARETURN) (.visitMaxs 0 0) (.visitEnd))) - _ (.visitEnd =class)] - (write-file (str current-class ".class") (.toByteArray =class))) + _ (.visitEnd =class) + bytecode (.toByteArray =class)] + (write-file (str current-class ".class") bytecode) + (load-class! (string/replace current-class #"/" ".") (str current-class ".class"))) )) (defn compile-field [writer class-name ?name body state] @@ -614,8 +581,10 @@ (.visitInsn Opcodes/RETURN) (.visitMaxs 0 0) (.visitEnd))) - (.visitEnd))] - (write-file (str current-class ".class") (.toByteArray =class))) + (.visitEnd)) + bytecode (.toByteArray =class)] + (write-file (str current-class ".class") bytecode) + (load-class! (string/replace current-class #"/" ".") (str current-class ".class"))) )) (defcompiler ^:private compile-def @@ -757,8 +726,10 @@ (.visitInsn Opcodes/ARETURN) (.visitMaxs 0 0) (.visitEnd)) - _ (.visitEnd =class)] - (write-file (str current-class ".class") (.toByteArray =class)) + _ (.visitEnd =class) + bytecode (.toByteArray =class)] + (write-file (str current-class ".class") bytecode) + (load-class! (string/replace current-class #"/" ".") (str current-class ".class")) ;; (apply prn 'LAMBDA ?scope ?args (->> (:mappings ?frame) ;; (map second) ;; (map :form) @@ -797,8 +768,8 @@ (.visitEnd)) (.visitEnd =class) (.mkdirs (java.io.File. parent-dir)) - (with-open [stream (java.io.BufferedOutputStream. (java.io.FileOutputStream. (str parent-dir "/" ?name ".class")))] - (.write stream (.toByteArray =class))))) + (write-file (str parent-dir "/" ?name ".class") (.toByteArray =class)) + (load-class! (string/replace (str parent-dir "/" ?name) #"/" ".") (str parent-dir "/" ?name ".class")))) (defcompiler ^:private compile-definterface [::&analyser/definterface [?package ?name] ?members] @@ -813,8 +784,8 @@ (.visitMethod =interface (+ Opcodes/ACC_PUBLIC Opcodes/ACC_ABSTRACT) ?method signature nil nil)) (.visitEnd =interface) (.mkdirs (java.io.File. parent-dir)) - (with-open [stream (java.io.BufferedOutputStream. (java.io.FileOutputStream. (str parent-dir "/" ?name ".class")))] - (.write stream (.toByteArray =interface))))) + (write-file (str parent-dir "/" ?name ".class") (.toByteArray =interface)) + (load-class! (string/replace (str parent-dir "/" ?name) #"/" ".") (str parent-dir "/" ?name ".class")))) (defcompiler ^:private compile-variant [::&analyser/variant ?tag ?members] @@ -856,12 +827,40 @@ ;; _ (prn 'tokens tokens) syntax (&parser/parse tokens) ;; _ (prn 'syntax syntax) - ann-syntax (&analyser/analyse module-name syntax) + ;; ann-syntax (&analyser/analyse module-name syntax) ;; _ (prn 'ann-syntax ann-syntax) - class-data (compile module-name ann-syntax)] - (write-file (str module-name ".class") class-data) + bytecode (compile module-name syntax)] + ;; (write-file (str module-name ".class") bytecode) + ;; (load-class! (string/replace module-name #"/" ".") (str module-name ".class")) nil)) +(defn quoted->token [quoted] + (prn 'quoted->token quoted) + (match quoted + [::&parser/string ?text] + {:form [::&analyser/variant "Text" (list {:form [::&analyser/literal ?text] + :type [::&type/object "java.lang.String" []]})] + :type [::&type/variant "Text" (list [::&type/object "java.lang.String" []])]} + + [::&parser/fn-call ?fn ?args] + (let [members* (quoted->token (cons ?fn ?args))] + {:form [::&analyser/variant "Form" (list members*)] + :type [::&type/variant "Form" (list (:type members*))]}) + + ([] :seq) + {:form [::&analyser/variant "Nil" '()] + :type [::&type/variant "Nil" '()]} + + ([head & tail] :seq) + (let [head* (quoted->token head) + tail* (quoted->token tail)] + {:form [::&analyser/variant "Cons" (list head* tail*)] + :type [::&type/variant "Nil" (list (:type head*) (:type tail*))]}))) + +(defcompiler compile-quote + [::&analyser/quote ?quoted] + (compile-form (assoc *state* :form (quoted->token ?quoted)))) + (let [+compilers+ [compile-literal compile-variant compile-tuple @@ -882,7 +881,8 @@ compile-defclass compile-definterface compile-import - compile-require]] + compile-require + compile-quote]] (defn ^:private compile-form [state] ;; (prn 'compile-form/state state) (or (some #(% state) +compilers+) @@ -894,18 +894,39 @@ (let [=class (doto (new ClassWriter ClassWriter/COMPUTE_MAXS) (.visit Opcodes/V1_5 (+ Opcodes/ACC_PUBLIC Opcodes/ACC_SUPER) (->class class-name) nil "java/lang/Object" nil)) - state {:class-name class-name - :writer =class - :form nil - :parent nil}] - (doseq [input inputs] - (when (not (compile-form (assoc state :form input))) - (assert false input))) + compiler-state {:class-name class-name + :writer =class + :form nil + :parent nil}] + (match ((repeat-m + (&analyser/with-scope class-name + (exec [ann-input &analyser/analyse-form + :let [_ (when (not (compile-form (assoc compiler-state :form ann-input))) + (assert false ann-input))]] + (return ann-input)))) + {:name class-name + :forms inputs + :deps {} + :imports {} + :defs {} + :defs-env {} + :lambda-scope [[] 0] + :env (list (&analyser/fresh-env 0)) + :types &type/+init+}) + [::&util/ok [?state ?forms]] + (if (empty? (:forms ?state)) + ?forms + (assert false (str "Unconsumed input: " (pr-str (:forms ?state))))) + + [::&util/failure ?message] + (assert false ?message)) + ;;; (.visitEnd =class) - (let [=array (.toByteArray =class)] - ;; (prn 'compile class-name =array) - =array)) - + (let [bytecode (.toByteArray =class)] + (write-file (str class-name ".class") bytecode) + (load-class! (string/replace class-name #"/" ".") (str class-name ".class")) + bytecode) + ) ;; (comment ;; (-> (java.io.File. "./") .toURL vector into-array java.net.URLClassLoader. (.loadClass "test2")) ;; (-> (java.io.File. "./") .toURL vector into-array java.net.URLClassLoader. (.loadClass "test2.Function")) diff --git a/src/lang/lexer.clj b/src/lang/lexer.clj index 870209503..9fa533dc5 100644 --- a/src/lang/lexer.clj +++ b/src/lang/lexer.clj @@ -52,7 +52,7 @@ ;; [Lexers] (def ^:private lex-white-space (lex-regex #"^(\s+)")) -(def +ident-re+ #"^([a-zA-Z\-\+\_\=!@$%^&*<>\.,/\\\|':][0-9a-zA-Z\-\+\_\=!@$%^&*<>\.,/\\\|':]*)") +(def +ident-re+ #"^([a-zA-Z\-\+\_\=!@$%^&*<>\.,/\\\|':\~][0-9a-zA-Z\-\+\_\=!@$%^&*<>\.,/\\\|':\~]*)") (do-template [ ] (def diff --git a/src/lang/parser.clj b/src/lang/parser.clj index 48f624ba4..34f3e70b4 100644 --- a/src/lang/parser.clj +++ b/src/lang/parser.clj @@ -71,6 +71,12 @@ =body (apply-m parse-form (list ?body))] (return [::def =name =body]))) +(defparser ^:private parse-defmacro + [::&lexer/list ([[::&lexer/ident "defmacro"] ?name ?body] :seq)] + (exec [=name (apply-m parse-form (list ?name)) + =body (apply-m parse-form (list ?body))] + (return [::defmacro =name =body]))) + (defparser ^:private parse-defdata [::&lexer/list ([[::&lexer/ident "defdata"] ?type & ?cases] :seq)] (exec [=type (apply-m parse-form (list ?type)) @@ -195,6 +201,7 @@ parse-record parse-lambda parse-def + parse-defmacro parse-defdata parse-if parse-do diff --git a/src/lang/util.clj b/src/lang/util.clj index e2edfb550..7eb431033 100644 --- a/src/lang/util.clj +++ b/src/lang/util.clj @@ -132,3 +132,28 @@ [::ok [(assoc state slot ?state) ?value]] _ =return)))) + +(defn ^:private normalize-char [char] + (case char + \* "_ASTER_" + \+ "_PLUS_" + \- "_DASH_" + \/ "_SLASH_" + \_ "_UNDERS_" + \% "_PERCENT_" + \$ "_DOLLAR_" + \! "_BANG_" + \' "_QUOTE_" + \` "_BQUOTE_" + \@ "_AT_" + \^ "_CARET_" + \& "_AMPERS_" + \= "_EQ_" + ;; default + char)) + +(defn normalize-ident [ident] + (reduce str "" (map normalize-char ident))) + +(defonce loader (doto (-> (java.io.File. "./") .toURL vector into-array java.net.URLClassLoader.) + (->> (prn 'loader)))) -- cgit v1.2.3