(.module: lux (lux (control ["ex" exception #+ exception:] pipe [monad #+ do]) (data [bit] [maybe] ["e" error #+ Error] [text "text/" Eq] text/format (coll [array])) [macro] [io #+ Process] [host #+ class: interface: object] (world [file #+ File])) (luxc [lang] [".C" io])) (type: #export JS Text) (type: #export Expression JS) (type: #export Statement JS) (host.import java/lang/Object (toString [] String)) (host.import java/lang/String (getBytes [String] #try (Array byte))) (host.import java/lang/Number (doubleValue [] double) (longValue [] Long)) (host.import java/lang/Integer (longValue [] Long)) (host.import java/lang/Long (intValue [] Integer)) (host.import java/lang/AbstractStringBuilder (append [String] AbstractStringBuilder)) (host.import java/lang/StringBuilder (new []) (toString [] String)) (host.import javax/script/ScriptEngine (eval [String] #try #? Object)) (host.import javax/script/ScriptEngineFactory (getScriptEngine [] ScriptEngine)) (host.import jdk/nashorn/api/scripting/NashornScriptEngineFactory (new [])) (host.import jdk/nashorn/api/scripting/NashornScriptEngine) (host.import jdk/nashorn/api/scripting/JSObject (isArray [] boolean) (isFunction [] boolean) (getMember [String] #? Object) (hasMember [String] boolean)) (host.import jdk/nashorn/api/scripting/AbstractJSObject) (host.import jdk/nashorn/api/scripting/ScriptObjectMirror (size [] int)) (host.import jdk/nashorn/internal/runtime/Undefined) (host.import java/util/Arrays (#static [t] copyOfRange [(Array t) int int] (Array t))) (type: #export Host {## #artifacts Artifacts ## #context [Text Nat] #interpreter ScriptEngine #module-buffer (Maybe StringBuilder) #program-buffer StringBuilder }) (def: #export (init _) (-> Top Host) {#interpreter (ScriptEngineFactory::getScriptEngine [] (NashornScriptEngineFactory::new [])) #module-buffer #.None #program-buffer (StringBuilder::new [])}) (def: #export module-js-name Text "module.js") (def: #export (init-module-buffer _) (-> Top (Meta Unit)) (function [compiler] (#e.Success [(update@ #.host (|>> (:! Host) (set@ #module-buffer (#.Some (StringBuilder::new []))) (:! Void)) compiler) []]))) (exception: #export No-Active-Module-Buffer) (exception: #export Cannot-Execute) (exception: #export Cannot-Evaluate) (def: #export module-buffer (Meta StringBuilder) (function [compiler] (case (|> compiler (get@ #.host) (:! Host) (get@ #module-buffer)) #.None ((lang.fail (No-Active-Module-Buffer "")) compiler) (#.Some module-buffer) (#e.Success [compiler module-buffer])))) (def: #export program-buffer (Meta StringBuilder) (function [compiler] (#e.Success [compiler (|> compiler (get@ #.host) (:! Host) (get@ #program-buffer))]))) (def: (execute code) (-> Expression (Meta Unit)) (function [compiler] (case (|> compiler (get@ #.host) (:! Host) (get@ #interpreter) (ScriptEngine::eval [code])) (#e.Error error) ((lang.fail (Cannot-Execute error)) compiler) (#e.Success _) (#e.Success [compiler []])))) (def: (::toString js-object) (-> Top JSObject) (object [] AbstractJSObject [] [] (AbstractJSObject (isFunction) boolean true) (AbstractJSObject (call [args (Array Object)]) Object (Object::toString [] (:! Object js-object))) )) (def: (::slice js-object value) (-> (-> Object JSObject) (Array Object) JSObject) (object [] AbstractJSObject [] [] (AbstractJSObject (isFunction) boolean true) (AbstractJSObject (call [args (Array Object)]) Object (:! Object (js-object (Arrays::copyOfRange [value (|> args (array.read +0) maybe.assume (:! Int)) (nat-to-int (array.size value))])))) )) (exception: #export Unknown-Member) (def: int-high-field Text "H") (def: int-low-field Text "L") (def: jvm-int (-> Nat Integer) (|>> (:! Long) (Long::intValue []))) (def: low-mask Nat (|> +1 (bit.shift-left +32) n/dec)) (def: high (-> Nat Nat) (bit.shift-right +32)) (def: low (-> Nat Nat) (bit.and low-mask)) (interface: IntValue (getValue [] Long)) (host.import luxc/lang/translation/js/IntValue (getValue [] Long)) (def: (js-int value) (-> Int JSObject) (object [] AbstractJSObject [IntValue] [] ## Methods (IntValue (getValue) Long (:! Long value)) (AbstractJSObject (getMember [member String]) Object (cond (text/= int-high-field member) (|> value int-to-nat high jvm-int) (text/= int-low-field member) (|> value int-to-nat low jvm-int) ## else (error! (Unknown-Member (format " member = " member "\n" "object(int) = " (%i value) "\n"))))))) (interface: StructureValue (getValue [] (Array Object))) (host.import luxc/lang/translation/js/StructureValue (getValue [] (Array Object))) (def: (js-structure value) (-> (Array Object) JSObject) (object [] AbstractJSObject [StructureValue] [] ## Methods (StructureValue (getValue) (Array Object) (:! (Array Object) value)) (AbstractJSObject (isArray) boolean true) (AbstractJSObject (getMember [member String]) Object (cond (text/= "toString" member) (:! Object (::toString value)) (text/= "length" member) (jvm-int (array.size value)) (text/= "slice" member) (let [js-object (: (-> Object JSObject) (|>> (cond> [(host.instance? (Array Object))] [(:! (Array Object)) js-structure] [(host.instance? Long)] [(:! Int) js-int] ## else [(:! JSObject)])))] (:! Object (::slice js-object value))) ## else (error! (Unknown-Member (format " member = " (:! Text member) "\n" "object(structure) = " (Object::toString [] (:! Object value)) "\n"))))) (AbstractJSObject (getSlot [idx int]) Object (|> value (array.read (|> idx (Integer::longValue []) (:! Nat))) maybe.assume (cond> [(host.instance? (Array Object))] [(:! (Array Object)) js-structure] [(host.instance? Long)] [(:! Int) js-int] ## else [(:! JSObject)]) (:! Object))) )) ## (def: (wrap-lux-object object) ## (-> Top JSObject) ## (if (host.instance? JSObject object) ## (lux-obj object) ## obj)) (def: (int js-object) (-> ScriptObjectMirror (Maybe Int)) (case [(JSObject::getMember [int-high-field] js-object) (JSObject::getMember [int-low-field] js-object)] (^multi [(#.Some high) (#.Some low)] (and (host.instance? Number high) (host.instance? Number low)) [[(Number::longValue [] (:! Number high)) (Number::longValue [] (:! Number low))] [high low]]) (#.Some (nat-to-int (n/+ (|> high (:! Int) int-to-nat (bit.shift-left +32)) (|> low (:! Int) int-to-nat)))) _ #.None)) (def: (extend-array by input) (All [a] (-> Nat (Array a) (Array a))) (let [size (array.size input)] (|> (array.new (n/+ by size)) (array.copy size +0 input +0)))) (def: (array element-parser js-object) (-> (-> Object (Error Top)) ScriptObjectMirror (Maybe (Array Object))) (if (JSObject::isArray [] js-object) (let [init-num-keys (int-to-nat (ScriptObjectMirror::size [] js-object))] (loop [num-keys init-num-keys idx +0 output (: (Array Object) (array.new init-num-keys))] (if (n/< num-keys idx) (let [idx-key (|> idx nat-to-int %i)] (case (JSObject::getMember idx-key js-object) (#.Some member) (case (element-parser member) (#e.Success parsed-member) (recur num-keys (n/inc idx) (array.write idx (:! Object parsed-member) output)) (#e.Error error) #.None) #.None (recur (n/inc num-keys) (n/inc idx) (extend-array +1 output)))) (#.Some output)))) #.None)) (exception: #export Unknown-Kind-Of-JS-Object) (exception: #export Null-Has-No-Lux-Representation) (def: (lux-object js-object) (-> Object (Error Top)) (`` (cond (host.null? js-object) (ex.throw Null-Has-No-Lux-Representation "") (host.instance? Integer js-object) (ex.return (Integer::longValue [] (:! Integer js-object))) (or (host.instance? java/lang/Boolean js-object) (host.instance? java/lang/String js-object)) (ex.return js-object) (host.instance? Number js-object) (ex.return (Number::doubleValue [] (:! Number js-object))) (~~ (do-template [ ] [(host.instance? js-object) (ex.return ( [] (:! js-object)))] [StructureValue StructureValue::getValue] [IntValue IntValue::getValue])) (host.instance? ScriptObjectMirror js-object) (let [js-object (:! ScriptObjectMirror js-object)] (case (int js-object) (#.Some value) (ex.return value) #.None (case (array lux-object js-object) (#.Some value) (ex.return value) #.None ## (JSObject::isFunction [] js-object) ## js-object ## else (ex.throw Unknown-Kind-Of-JS-Object (Object::toString [] (:! Object js-object)))))) ## else (ex.throw Unknown-Kind-Of-JS-Object (Object::toString [] (:! Object js-object)))))) (def: #export (eval code) (-> Expression (Meta Top)) (function [compiler] (case (|> compiler (:! Host) (get@ #interpreter) (ScriptEngine::eval [code])) (#e.Error error) ((lang.fail (Cannot-Evaluate error)) compiler) (#e.Success output) (case output #.None (#e.Success [compiler []]) (#.Some output) (case (lux-object output) (#e.Success parsed-output) (#e.Success [compiler parsed-output]) (#e.Error error) (#e.Error error)))))) (def: #export unit Text "\u0000") (def: (module-name module) (-> Text Text) (|> module (text.replace-all "/" "$") (text.replace-all "-" "_"))) (def: (definition-name [module name]) (-> Ident Text) (format (module-name module) "$" (lang.normalize-name name))) (def: #export (save-definition name code) (-> Ident Expression (Meta Unit)) (do macro.Monad [#let [js-definition (format "var " (definition-name name) " = " code ";\n")] module-buffer module-buffer #let [_ (AbstractStringBuilder::append [js-definition] module-buffer)]] (execute js-definition))) (def: #export (save-module! target) (-> File (Meta (Process Unit))) (do macro.Monad [module macro.current-module-name module-buffer module-buffer program-buffer program-buffer #let [module-code (StringBuilder::toString [] module-buffer) _ (AbstractStringBuilder::append [(format module-code "\n")] program-buffer)]] (wrap (ioC.write target (format module "/" module-js-name) (|> module-code (String::getBytes ["UTF-8"]) e.assume)))))