aboutsummaryrefslogtreecommitdiff
path: root/luxc/src/lux/compiler/js/rt.clj
diff options
context:
space:
mode:
Diffstat (limited to 'luxc/src/lux/compiler/js/rt.clj')
-rw-r--r--luxc/src/lux/compiler/js/rt.clj1058
1 files changed, 1058 insertions, 0 deletions
diff --git a/luxc/src/lux/compiler/js/rt.clj b/luxc/src/lux/compiler/js/rt.clj
new file mode 100644
index 000000000..b2104cb1b
--- /dev/null
+++ b/luxc/src/lux/compiler/js/rt.clj
@@ -0,0 +1,1058 @@
+(ns lux.compiler.js.rt
+ (:require (clojure [string :as string]
+ [set :as set]
+ [template :refer [do-template]])
+ clojure.core.match
+ clojure.core.match.array
+ (lux [base :as & :refer [|do return* return |let |case]]
+ [type :as &type]
+ [lexer :as &lexer]
+ [parser :as &parser]
+ [analyser :as &analyser]
+ [optimizer :as &o]
+ [host :as &host])
+ [lux.analyser.base :as &a]
+ [lux.compiler.js.base :as &&]))
+
+(def ^:private const-none (str "[0,null," &&/unit "]"))
+(defn ^:private make-some [value]
+ (str "[1,''," value "]"))
+
+(def ^:private adt-methods
+ {"product_getLeft" (str "(function LuxRT$product_getLeft(product,index) {"
+ "var index_min_length = (index+1);"
+ "if(product.length > index_min_length) {"
+ ;; No need for recursion
+ "return product[index];"
+ "}"
+ "else {"
+ ;; Needs recursion
+ "return LuxRT$product_getLeft(product[product.length - 1], (index_min_length - product.length));"
+ "}"
+ "})")
+ "product_getRight" (str "(function LuxRT$product_getRight(product,index) {"
+ "var index_min_length = (index+1);"
+ "if(product.length === index_min_length) {"
+ ;; Last element.
+ "return product[index];"
+ "}"
+ "else if(product.length < index_min_length) {"
+ ;; Needs recursion
+ "return LuxRT$product_getRight(product[product.length - 1], (index_min_length - product.length));"
+ "}"
+ "else {"
+ ;; Must slice
+ "return product.slice(index);"
+ "}"
+ "})")
+ "sum_get" (let [no-match "return null;"
+ extact-match "return sum[2];"
+ recursion-test (str (str "if(sum[1] === '') {"
+ ;; Must recurse.
+ "return LuxRT$sum_get(sum[2], (wantedTag - sum[0]), wantsLast);"
+ "}"
+ "else { " no-match " }"))]
+ (str "(function LuxRT$sum_get(sum,wantedTag,wantsLast) {"
+ "if(wantedTag === sum[0]) {"
+ (str "if(sum[1] === wantsLast) {" extact-match "}"
+ "else {" recursion-test "}")
+ "}"
+ "else if(wantedTag > sum[0]) {" recursion-test "}"
+ "else { " no-match " }"
+ "})"))
+ })
+
+(def ^:private i64-methods
+ {"TWO_PWR_16" "(1 << 16)"
+ "TWO_PWR_32" "((1 << 16) * (1 << 16))"
+ "TWO_PWR_64" "(((1 << 16) * (1 << 16)) * ((1 << 16) * (1 << 16)))"
+ "TWO_PWR_63" "((((1 << 16) * (1 << 16)) * ((1 << 16) * (1 << 16))) / 2)"
+ "getLowBitsUnsigned" (str "(function LuxRT$getLowBitsUnsigned(i64) {"
+ "return (i64.L >= 0) ? i64.L : (LuxRT$TWO_PWR_32 + i64.L);"
+ "})")
+ "toNumberI64" (str "(function LuxRT$toNumberI64(i64) {"
+ "return (i64.H * LuxRT$TWO_PWR_32) + LuxRT$getLowBitsUnsigned(i64);"
+ "})")
+ "fromNumberI64" (str "(function LuxRT$fromNumberI64(num) {"
+ (str "if(isNaN(num)) {"
+ "return LuxRT$ZERO;"
+ "}")
+ (str "else if(num <= -LuxRT$TWO_PWR_63) {"
+ "return LuxRT$MIN_VALUE_I64;"
+ "}")
+ (str "else if((num + 1) >= LuxRT$TWO_PWR_63) {"
+ "return LuxRT$MAX_VALUE_I64;"
+ "}")
+ (str "else if(num < 0) {"
+ "return LuxRT$negateI64(LuxRT$fromNumberI64(-num));"
+ "}")
+ (str "else {"
+ "return LuxRT$makeI64((num / LuxRT$TWO_PWR_32), (num % LuxRT$TWO_PWR_32));"
+ "}")
+ "})")
+ "makeI64" (str "(function LuxRT$makeI64(high,low) {"
+ "return { H: (high|0), L: (low|0)};"
+ "})")
+ "MIN_VALUE_I64" "{ H: (0x80000000|0), L: (0|0)}"
+ "MAX_VALUE_I64" "{ H: (0x7FFFFFFF|0), L: (0xFFFFFFFF|0)}"
+ "ONE" "{ H: (0|0), L: (1|0)}"
+ "ZERO" "{ H: (0|0), L: (0|0)}"
+ "notI64" (str "(function LuxRT$notI64(i64) {"
+ "return LuxRT$makeI64(~i64.H,~i64.L);"
+ "})")
+ "negateI64" (str "(function LuxRT$negateI64(i64) {"
+ (str "if(LuxRT$eqI64(LuxRT$MIN_VALUE_I64,i64)) {"
+ "return LuxRT$MIN_VALUE_I64;"
+ "}")
+ (str "else {"
+ "return LuxRT$addI64(LuxRT$notI64(i64),LuxRT$ONE);"
+ "}")
+ "})")
+ "eqI64" (str "(function LuxRT$eqI64(l,r) {"
+ "return (l.H === r.H) && (l.L === r.L);"
+ "})")
+ "addI64" (str "(function LuxRT$addI64(l,r) {"
+ "var l48 = l.H >>> 16;"
+ "var l32 = l.H & 0xFFFF;"
+ "var l16 = l.L >>> 16;"
+ "var l00 = l.L & 0xFFFF;"
+
+ "var r48 = r.H >>> 16;"
+ "var r32 = r.H & 0xFFFF;"
+ "var r16 = r.L >>> 16;"
+ "var r00 = r.L & 0xFFFF;"
+
+ "var x48 = 0, x32 = 0, x16 = 0, x00 = 0;"
+ "x00 += l00 + r00;"
+ "x16 += x00 >>> 16;"
+ "x00 &= 0xFFFF;"
+ "x16 += l16 + r16;"
+ "x32 += x16 >>> 16;"
+ "x16 &= 0xFFFF;"
+ "x32 += l32 + r32;"
+ "x48 += x32 >>> 16;"
+ "x32 &= 0xFFFF;"
+ "x48 += l48 + r48;"
+ "x48 &= 0xFFFF;"
+
+ "return LuxRT$makeI64((x48 << 16) | x32, (x16 << 16) | x00);"
+ "})")
+ "subI64" (str "(function LuxRT$subI64(l,r) {"
+ "return LuxRT$addI64(l,LuxRT$negateI64(r));"
+ "})")
+ "mulI64" (str "(function LuxRT$mulI64(l,r) {"
+ "if (l.H < 0) {"
+ (str "if (r.H < 0) {"
+ ;; Both are negative
+ "return LuxRT$mulI64(LuxRT$negateI64(l),LuxRT$negateI64(r));"
+ "}"
+ "else {"
+ ;; Left is negative
+ "return LuxRT$negateI64(LuxRT$mulI64(LuxRT$negateI64(l),r));"
+ "}")
+ "}"
+ "else if (r.H < 0) {"
+ ;; Right is negative
+ "return LuxRT$negateI64(LuxRT$mulI64(l,LuxRT$negateI64(r)));"
+ "}"
+ ;; Both are positive
+ "else {"
+ "var l48 = l.H >>> 16;"
+ "var l32 = l.H & 0xFFFF;"
+ "var l16 = l.L >>> 16;"
+ "var l00 = l.L & 0xFFFF;"
+
+ "var r48 = r.H >>> 16;"
+ "var r32 = r.H & 0xFFFF;"
+ "var r16 = r.L >>> 16;"
+ "var r00 = r.L & 0xFFFF;"
+
+ "var x48 = 0, x32 = 0, x16 = 0, x00 = 0;"
+ "x00 += l00 * r00;"
+ "x16 += x00 >>> 16;"
+ "x00 &= 0xFFFF;"
+ "x16 += l16 * r00;"
+ "x32 += x16 >>> 16;"
+ "x16 &= 0xFFFF;"
+ "x16 += l00 * r16;"
+ "x32 += x16 >>> 16;"
+ "x16 &= 0xFFFF;"
+ "x32 += l32 * r00;"
+ "x48 += x32 >>> 16;"
+ "x32 &= 0xFFFF;"
+ "x32 += l16 * r16;"
+ "x48 += x32 >>> 16;"
+ "x32 &= 0xFFFF;"
+ "x32 += l00 * r32;"
+ "x48 += x32 >>> 16;"
+ "x32 &= 0xFFFF;"
+ "x48 += (l48 * r00) + (l32 * r16) + (l16 * r32) + (l00 * r48);"
+ "x48 &= 0xFFFF;"
+
+ "return LuxRT$makeI64((x48 << 16) | x32, (x16 << 16) | x00);"
+ "}"
+ "})")
+ "divI64" (str "(function LuxRT$divI64(l,r) {"
+ (str "if((r.H === 0) && (r.L === 0)) {"
+ ;; Special case: R = 0
+ "throw new Error('Cannot divide by zero!');"
+ "}"
+ "else if((l.H === 0) && (l.L === 0)) {"
+ ;; Special case: L = 0
+ "return l;"
+ "}")
+ (str "if(LuxRT$eqI64(l,LuxRT$MIN_VALUE_I64)) {"
+ ;; Special case: L = MIN
+ (str "if(LuxRT$eqI64(r,LuxRT$ONE) || LuxRT$eqI64(r,LuxRT$negateI64(LuxRT$ONE))) {"
+ ;; Special case: L = MIN, R = 1|-1
+ "return LuxRT$MIN_VALUE_I64;"
+ "}"
+ ;; Special case: L = R = MIN
+ "else if(LuxRT$eqI64(r,LuxRT$MIN_VALUE_I64)) {"
+ "return LuxRT$ONE;"
+ "}"
+ ;; Special case: L = MIN
+ "else {"
+ "var halfL = LuxRT$shrI64(l,1);"
+ "var approx = LuxRT$shlI64(LuxRT$divI64(halfL,r),LuxRT$ONE);"
+ (str "if((approx.H === 0) && (approx.L === 0)) {"
+ (str "if(r.H < 0) {"
+ "return LuxRT$ONE;"
+ "}"
+ "else {"
+ "return LuxRT$negateI64(LuxRT$ONE);"
+ "}")
+ "}"
+ "else {"
+ "var rem = LuxRT$subI64(l,LuxRT$mulI64(r,approx));"
+ "return LuxRT$addI64(approx,LuxRT$divI64(rem,r));"
+ "}")
+ "}")
+ "}"
+ "else if(LuxRT$eqI64(r,LuxRT$MIN_VALUE_I64)) {"
+ ;; Special case: R = MIN
+ "return LuxRT$makeI64(0,0);"
+ "}")
+ ;; Special case: negatives
+ (str "if(l.H < 0) {"
+ (str "if(r.H < 0) {"
+ ;; Both are negative
+ "return LuxRT$divI64(LuxRT$negateI64(l),LuxRT$negateI64(r));"
+ "}"
+ "else {"
+ ;; Only L is negative
+ "return LuxRT$negateI64(LuxRT$divI64(LuxRT$negateI64(l),r));"
+ "}")
+ "}"
+ "else if(r.H < 0) {"
+ ;; R is negative
+ "return LuxRT$negateI64(LuxRT$divI64(l,LuxRT$negateI64(r)));"
+ "}")
+ ;; Common case
+ (str "var res = LuxRT$ZERO;"
+ "var rem = l;"
+ (str "while(LuxRT$ltI64(r,rem) || LuxRT$eqI64(r,rem)) {"
+ "var approx = Math.max(1, Math.floor(LuxRT$toNumberI64(rem) / LuxRT$toNumberI64(r)));"
+ "var log2 = Math.ceil(Math.log(approx) / Math.LN2);"
+ "var delta = (log2 <= 48) ? 1 : Math.pow(2, log2 - 48);"
+ "var approxRes = LuxRT$fromNumberI64(approx);"
+ "var approxRem = LuxRT$mulI64(approxRes,r);"
+ (str "while((approxRem.H < 0) || LuxRT$ltI64(rem,approxRem)) {"
+ "approx -= delta;"
+ "approxRes = LuxRT$fromNumberI64(approx);"
+ "approxRem = LuxRT$mulI64(approxRes,r);"
+ "}")
+ (str "if((approxRes.H === 0) && (approxRes.L === 0)) {"
+ "approxRes = LuxRT$ONE;"
+ "}")
+ "res = LuxRT$addI64(res,approxRes);"
+ "rem = LuxRT$subI64(rem,approxRem);"
+ "}")
+ "return res;")
+ "})")
+ "remI64" (str "(function LuxRT$remI64(l,r) {"
+ "return LuxRT$subI64(l,LuxRT$mulI64(LuxRT$divI64(l,r),r));"
+ "})")
+ "ltI64" (str "(function LuxRT$ltI64(l,r) {"
+ "var ln = l.H < 0;"
+ "var rn = r.H < 0;"
+ "if(ln && !rn) { return true; }"
+ "if(!ln && rn) { return false; }"
+ "return (LuxRT$subI64(l,r).H < 0);"
+ "})")
+ "encodeI64" (str "(function LuxRT$encodeI64(input) {"
+ ;; If input = 0
+ (str "if((input.H === 0) && (input.L === 0)) {"
+ "return '0';"
+ "}")
+ ;; If input < 0
+ (str "if(input.H < 0) {"
+ (str "if(LuxRT$eqI64(input,LuxRT$MIN_VALUE_I64)) {"
+ "var radix = LuxRT$makeI64(0,10);"
+ "var div = LuxRT$divI64(input,radix);"
+ "var rem = LuxRT$subI64(LuxRT$mulI64(div,radix),input);"
+ "return LuxRT$encodeI64(div).concat(rem.L+'');"
+ "}")
+ (str "else {"
+ "return '-'.concat(LuxRT$encodeI64(LuxRT$negateI64(input)));"
+ "}")
+ "}")
+ ;; If input > 0
+ (str "var chunker = LuxRT$makeI64(0,1000000);"
+ "var rem = input;"
+ "var result = '';"
+ "while(true) {"
+ (str "var remDiv = LuxRT$divI64(rem,chunker);"
+ "var chunk = LuxRT$subI64(rem,LuxRT$mulI64(remDiv,chunker));"
+ "var digits = (chunk.L >>> 0)+'';"
+ "rem = remDiv;"
+ (str "if((rem.H === 0) && (rem.L === 0)) {"
+ "return digits.concat(result);"
+ "}"
+ "else {"
+ (str "while(digits.length < 6) {"
+ "digits = '0' + digits;"
+ "}")
+ "result = '' + digits + result;"
+ "}"))
+ "}")
+ "})")
+ "decodeI64" (str "(function LuxRT$decodeI64(input) {"
+ "input = LuxRT$clean_separators(input);"
+ (str "if(/^-?\\d+$/.exec(input)) {"
+ (str "var isNegative = (input.charAt(0) == '-');"
+ "var sign = isNegative ? -1 : 1;"
+ "input = isNegative ? input.substring(1) : input;"
+
+ "var chunkPower = LuxRT$fromNumberI64(Math.pow(10, 8));"
+ "var result = LuxRT$ZERO;"
+ (str "for (var i = 0; i < input.length; i += 8) {"
+ "var size = Math.min(8, input.length - i);"
+ "var value = parseInt(input.substring(i, i + size), 10);"
+ (str "if (size < 8) {"
+ "var power = LuxRT$fromNumberI64(Math.pow(10, size));"
+ "result = LuxRT$addI64(LuxRT$mulI64(result,power),LuxRT$fromNumberI64(value));"
+ "}"
+ "else {"
+ "result = LuxRT$addI64(LuxRT$mulI64(result,chunkPower),LuxRT$fromNumberI64(value));"
+ "}")
+ "}")
+ "result = LuxRT$mulI64(result,LuxRT$fromNumberI64(sign));"
+ (str "return " (make-some "result") ";")
+ )
+ "}"
+ "else {"
+ (str "return " const-none ";")
+ "}")
+ "})")
+ })
+
+(def ^:private n64-methods
+ {"divWord" (str "(function LuxRT$divWord(result, n, d) {"
+ "var dLong = LuxRT$makeI64(0,d);"
+ (str "if (LuxRT$eqI64(dLong,LuxRT$ONE)) {"
+ (str "result[0] = n.L;"
+ "result[1] = 0;"
+ "return")
+ "}"
+ "else {"
+ ;; Approximate the quotient and remainder
+ (str "var q = LuxRT$divI64(LuxRT$ushrI64(n,1),LuxRT$ushrI64(dLong,1));"
+ "var r = LuxRT$subI64(n,LuxRT$mulI64(q,dLong));"
+ ;; Correct the approximation
+ (str "while(LuxRT$ltI64(r,LuxRT$ZERO)) {"
+ "r = LuxRT$addI64(r,dLong);"
+ "q = LuxRT$subI64(q,LuxRT$ONE);"
+ "}")
+ (str "while(LuxRT$ltI64(dLong,r) || LuxRT$eqI64(dLong,r)) {"
+ "r = LuxRT$subI64(r,dLong);"
+ "q = LuxRT$addI64(q,LuxRT$ONE);"
+ "}")
+ "result[0] = q.L;"
+ "result[1] = r.L;"
+ )
+ "}")
+ "})")
+ "primitiveShiftLeftBigInt" (str "(function LuxRT$primitiveShiftLeftBigInt(input,shift) {"
+ "var output = input.slice();"
+ "var shift2 = 32 - shift;"
+ (str "for(var i = 0, c = output[i], m = (i + (input.length - 1)); i < m; i++) {"
+ "var b = c;"
+ "c = output[i+1];"
+ "output[i] = (b << shift) | (c >>> shift2);"
+ "}")
+ "output[(input.length - 1)] <<= shift;"
+ "return output;"
+ "})")
+ "primitiveShiftRightBigInt" (str "(function LuxRT$primitiveShiftRightBigInt(input,shift) {"
+ "var output = input.slice();"
+ "var shift2 = 32 - shift;"
+ (str "for(var i = (input.length - 1), c = output[i]; i > 0; i--) {"
+ "var b = c;"
+ "c = output[i-1];"
+ "output[i] = (c << shift2) | (b >>> shift);"
+ "}")
+ "output[0] >>>= shift;"
+ "return output;"
+ "})")
+ "shiftLeftBigInt" (str "(function LuxRT$shiftLeftBigInt(input,shift) {"
+ "var shiftInts = shift >>> 5;"
+ "var shiftBits = shift & 0x1F;"
+ "var bitsInHighWord = LuxRT$countI64(LuxRT$makeI64(input[0],0));"
+ (str "if(shift <= (32 - bitsInHighWord)) {"
+ "var shifted = LuxRT$shlI64(LuxRT$makeI64(input[0],input[1]),shiftBits);"
+ "return [shifted.H,shifted.L];"
+ "}")
+ "var inputLen = input[0] === 0 ? 1 : 2;"
+ "var newLen = inputLen + shiftInts + 1;"
+ (str "if(shiftBits <= (32 - bitsInHighWord)) {"
+ "newLen--;"
+ "}")
+ (str "if(input.length < newLen) {"
+ ;; The array must grow
+ "input = [0|0,input[0],input[1]];"
+ "}")
+ (str "if(nBits == 0) {"
+ "return input;"
+ "}")
+ (str "if(shiftBits <= (32 - bitsInHighWord)) {"
+ "return LuxRT$primitiveShiftLeftBigInt(input,shiftBits);"
+ "}"
+ "else {"
+ "return LuxRT$primitiveShiftRightBigInt(input,(32 - shiftBits));"
+ "}")
+ "})")
+ "shiftRightBigInt" (str "(function LuxRT$shiftRightBigInt(input,shift) {"
+ "var shiftInts = shift >>> 5;"
+ "var shiftBits = shift & 0x1F;"
+ "if(shiftBits === 0) { return input; }"
+ "var bitsInHighWord = LuxRT$countI64(LuxRT$makeI64(input[0],0));"
+ (str "if(shiftBits >= bitsInHighWord) {"
+ "return LuxRT$primitiveShiftLeftBigInt(input,(32-shiftBits));"
+ "}"
+ "else {"
+ "return LuxRT$primitiveShiftRightBigInt(input,shiftBits);"
+ "}")
+ "})")
+ "mulsubBigInt" (str "(function LuxRT$mulsubBigInt(q, a, x, len, offset) {"
+ "var xLong = LuxRT$makeI64(0,x);"
+ "var carry = LuxRT$ZERO;"
+ "offset += len;"
+ (str "for (var j = len-1; j >= 0; j--) {"
+ "var product = LuxRT$addI64(LuxRT$mulI64(LuxRT$makeI64(0,a[j]),xLong),carry);"
+ "var difference = LuxRT$subI64(LuxRT$makeI64(0,q[offset]),product);"
+ "carry = LuxRT$addI64(LuxRT$ushrI64(product,32),((difference.L > ~product.L) ? LuxRT$ONE : LuxRT$ZERO));"
+ "}")
+ "return carry.L;"
+ "})")
+ "divadd" (str "(function LuxRT$divadd(a, result, offset) {"
+ "var carry = LuxRT$ZERO;"
+ (str "for (var j = a.length - 1; j >= 0; j--) {"
+ "var sum = LuxRT$addI64(LuxRT$addI64(LuxRT$makeI64(0,a[j]),LuxRT$makeI64(0,result[j+offset])),carry);"
+ "result[j+offset] = sum.L;"
+ "carry = LuxRT$ushrI64(sum,32);"
+ "}")
+ "return carry.L;"
+ "})")
+ "normalizeBigInt" (str "(function LuxRT$normalizeBigInt(input) {"
+ (str "if(input[0] !== 0) {"
+ "return LuxRT$makeI64(input[0],input[1]);"
+ "}"
+ "else {"
+ (str "var numZeros = 0;"
+ (str "do {"
+ "numZeros++;"
+ "} while(numZeros < input.length && input[numZeros] == 0);")
+ "var tempInput = input.slice(input.length-Math.max(2,input.length-numZeros));"
+ "return LuxRT$makeI64(tempInput[0],tempInput[1]);")
+ "}")
+ "})")
+ "divideOneWord" (str "(function LuxRT$divideOneWord(subject,param) {"
+ (str "var divLong = LuxRT$makeI64(0,param);"
+ ;; Special case of one word dividend
+ (str "if(subject.H === 0) {"
+ (str "var remValue = LuxRT$makeI64(0,subject.L);"
+ "var quotient = LuxRT$divI64(remValue,divLong);"
+ "var remainder = LuxRT$subI64(remValue,LuxRT$mulI64(quotient.L,divLong));"
+ "return [quotient,remainder];")
+ "}")
+ "var quotient = [0|0,0|0];"
+ ;; Normalize the divisor
+ "var shift = 32 - LuxRT$countI64(LuxRT$makeI64(0,param));"
+ "var rem = subject.H;"
+ "var remLong = LuxRT$makeI64(0,rem);"
+ (str "if(LuxRT$ltI64(remLong,divLong)) {"
+ "quotient[0] = 0|0;"
+ "}"
+ "else {"
+ "quotient[0] = LuxRT$divI64(remLong,divLong).L;"
+ "rem = LuxRT$subI64(remLong,LuxRT$mulI64(quotient[0],divLong)).L;"
+ "remLong = LuxRT$makeI64(0,rem);"
+ "}")
+ "var remBI = [subject.H,subject.L];"
+ "var xlen = 2;"
+ "var qWord = [0|0,0|0];"
+ (str "while(--xlen > 0) {"
+ "var dividendEstimate = LuxRT$orI64(LuxRT$shlI64(remLong,32),LuxRT$makeI64(0,remBI[2 - xlen]));"
+ (str "if(dividendEstimate >= 0) {"
+ "var highWord = LuxRT$divI64(dividendEstimate,divLong);"
+ "qWord[0] = highWord.L;"
+ "qWord[1] = LuxRT$subI64(dividendEstimate,LuxRT$mulI64(highWord,divLong)).L;"
+ "}"
+ "else {"
+ "LuxRT$divWord(qWord, dividendEstimate, param);"
+ "}")
+ "quotient[2 - xlen] = qWord[0];"
+ "rem = qWord[1];"
+ "remLong = LuxRT$makeI64(0,rem);"
+ "}")
+ ;; Unnormalize
+ (str "if(shift > 0) {"
+ "rem %= divisor;"
+ "remBI[0] = rem;"
+ "}"
+ "else {"
+ "remBI[0] = rem;"
+ "}")
+ "var quotI64 = LuxRT$normalizeBigInt(quotient);"
+ "var remI64 = LuxRT$makeI64(remBI[0],remBI[1]);"
+ "return [quotI64,remI64];")
+ "})")
+ "divmodBigInt" (str "(function LuxRT$divmodBigInt(subject,param) {"
+ (str "if(LuxRT$eqI64(param,LuxRT$ZERO)) {"
+ "throw new Error('Cannot divide by zero!');"
+ "}")
+ (str "if(LuxRT$eqI64(subject,LuxRT$ZERO)) {"
+ "return [LuxRT$ZERO, LuxRT$ZERO];"
+ "}")
+ (str "if(LuxRT$ltN64(subject,param)) {"
+ "return [LuxRT$ZERO, subject];"
+ "}")
+ (str "if(LuxRT$eqI64(subject,param)) {"
+ "return [LuxRT$ONE, LuxRT$ZERO];"
+ "}")
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (str "if (param.H === 0) {"
+ "return LuxRT$divideOneWord(subject,param.L);;"
+ "}")
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ "var divisor = param;"
+ "var remainder = subject.H === 0 ? [0|0,subject.L] : [0|0,subject.H,subject.L];"
+ "var paramLength = param.H === 0 ? 1 : 2;"
+ "var subjLength = subject.H === 0 ? 1 : 2;"
+ "var limit = subjLength - paramLength + 1;"
+ "var quotient = (limit === 1) ? [0|0] : [0|0,0|0];"
+ ;; Normalize the divisor
+ "var shift = 32 - LuxRT$countI64(LuxRT$makeI64(divisor.H,0));"
+ (str "if(shift > 0) {"
+ "divisor = LuxRT$shlI64(divisor,shift);"
+ "remainder = LuxRT$shiftLeftBigInt(remainder,shift);"
+ "}")
+ (str "if((remainder.length-1) === subjLength) {"
+ "remainder[0] = 0;"
+ "}")
+ "var dh = divisor.H;"
+ "var dhLong = LuxRT$makeI64(0,dh);"
+ "var dl = divisor.L;"
+ "var qWord = [0|0,0|0];"
+ ;; D2 Initialize j
+ (str "for(var j = 0; j < limit; j++) {"
+ ;; D3 Calculate qhat
+ ;; estimate qhat
+ "var qhat = 0;"
+ "var qrem = 0;"
+ "var skipCorrection = false;"
+ "var nh = remainder[j];"
+ "var nh2 = nh + 0x80000000;"
+ "var nm = remainder[j+1];"
+ (str "if(nh == dh) {"
+ (str "qhat = ~0;"
+ "qrem = nh + nm;"
+ "skipCorrection = (qrem + 0x80000000) < nh2;")
+ "}"
+ "else {"
+ (str "var nChunk = LuxRT$orI64(LuxRT$shlI64(LuxRT$fromNumberI64(nh),32),LuxRT$fromNumberI64(nm));")
+ (str "if(LuxRT$ltI64(LuxRT$ZERO,nChunk) || LuxRT$eqI64(LuxRT$ZERO,nChunk)) {"
+ (str "qhat = LuxRT$divI64(nChunk,dhLong).L;"
+ "qrem = LuxRT$subI64(nChunk,LuxRT$mulI64(qhat, dhLong)).L;")
+ "}"
+ "else {"
+ (str "LuxRT$divWord(qWord, nChunk, dh);"
+ "qhat = qWord[0];"
+ "qrem = qWord[1];"
+ )
+ "}")
+ "if(qhat == 0) { continue; }"
+ (str "if(!skipCorrection) {"
+ ;; Correct qhat
+ (str "var qremLong = LuxRT$makeI64(0,qrem);"
+ "var dlLong = LuxRT$makeI64(0,dl);"
+ "var nl = LuxRT$makeI64(0,remainder[j+2]);"
+ "var rs = LuxRT$orI64(LuxRT$shlI64(qremLong,32),nl);"
+ "var estProduct = LuxRT$mulI64(dlLong,LuxRT$makeI64(0,qhat));"
+ (str "if(LuxRT$ltN64(rs,estProduct)) {"
+ (str "qhat--;"
+ "qrem = LuxRT$addI64(qremLong,dhLong).L;"
+ "qremLong = LuxRT$makeI64(0,qrem);"
+ (str "if(LuxRT$ltI64(dhLong,qremLong) || LuxRT$eqI64(dhLong,qremLong)) {"
+ (str "estProduct = LuxRT$mulI64(dlLong,LuxRT$makeI64(0,qhat));"
+ "rs = LuxRT$orI64(LuxRT$shlI64(qremLong,32),nl);"
+ "if(LuxRT$ltN64(rs,estProduct)) { qhat--; }")
+ "}"))
+ "}")
+ )
+ "}")
+ ;; D4 Multiply and subtract
+ "remainder[j] = 0;"
+ "var borrow = LuxRT$mulsubBigInt(remainder, divisor, qhat, paramLength, j);"
+ ;; D5 Test remainder
+ (str "if((borrow + 0x80000000) > nh2) {"
+ ;; D6 Add back
+ "LuxRT$divadd(divisor, remainder, j+1);"
+ "qhat--;"
+ "}")
+ ;; Store the quotient digit
+ "quotient[j] = qhat;"
+ "}")
+ "}") ;; D7 loop on j
+ ;; D8 Unnormalize
+ "if(shift > 0) { remainder = LuxRT$shiftRightBigInt(remainder,shift); }"
+ "return [LuxRT$normalizeBigInt(quotient), LuxRT$normalizeBigInt(remainder)];"
+ "})")
+ "encodeN64" (str "(function LuxRT$encodeN64(input) {"
+ (str "if(input.H < 0) {"
+ ;; Too big
+ "var lastDigit = LuxRT$remI64(input, LuxRT$makeI64(0,10));"
+ "var minusLastDigit = LuxRT$divI64(input, LuxRT$makeI64(0,10));"
+ "return '+'.concat(LuxRT$encodeI64(minusLastDigit)).concat(LuxRT$encodeI64(lastDigit));"
+ "}"
+ "else {"
+ ;; Small enough
+ "return '+'.concat(LuxRT$encodeI64(input));"
+ "}")
+ "})")
+ "decodeN64" (str "(function LuxRT$decodeN64(input) {"
+ "input = LuxRT$clean_separators(input);"
+ (str "if(/^\\+\\d+$/.exec(input)) {"
+ (str "input = input.substring(1);")
+ (str "if(input.length <= 18) {"
+ ;; Short enough...
+ "return LuxRT$decodeI64(input);"
+ "}"
+ "else {"
+ ;; Too long
+ (str "var prefix = LuxRT$decodeI64(input.substring(0, input.length-1))[2];"
+ "var suffix = LuxRT$decodeI64(input.charAt(input.length-1))[2];"
+ "var total = LuxRT$addI64(LuxRT$mulI64(prefix,LuxRT$fromNumberI64(10)),suffix);"
+ (str "if(LuxRT$ltN64(total,prefix)) {"
+ (str "return " const-none ";")
+ "}"
+ "else {"
+ (str "return " (make-some "total") ";")
+ "}"))
+ "}")
+ "}"
+ "else {"
+ (str "return " const-none ";")
+ "}")
+ "})")
+ "divN64" (str "(function LuxRT$divN64(l,r) {"
+ (str "if(LuxRT$ltI64(r,LuxRT$ZERO)) {"
+ (str "if(LuxRT$ltN64(l,r)) {"
+ "return LuxRT$ZERO;"
+ "}"
+ "else {"
+ "return LuxRT$ONE;"
+ "}")
+ "}"
+ "else if(LuxRT$ltI64(LuxRT$ZERO,l)) {"
+ "return LuxRT$divI64(l,r);"
+ "}"
+ "else {"
+ (str "if(LuxRT$eqI64(LuxRT$ZERO,r)) {"
+ "throw new Error('Cannot divide by zero!');"
+ "}"
+ "else {"
+ (str "if(LuxRT$ltI64(l,r)) {"
+ "return LuxRT$ZERO;"
+ "}"
+ "else {"
+ "return LuxRT$divmodBigInt(l,r)[0];"
+ "}")
+ "}")
+ "}")
+ "})")
+ "remN64" (str "(function LuxRT$remN64(l,r) {"
+ (str "if(LuxRT$ltI64(l,LuxRT$ZERO) || LuxRT$ltI64(r,LuxRT$ZERO)) {"
+ (str "if(LuxRT$ltN64(l,r)) {"
+ "return l;"
+ "}"
+ "else {"
+ "return LuxRT$divmodBigInt(l,r)[1];"
+ "}")
+ "}"
+ "else {"
+ "return LuxRT$remI64(l,r);"
+ "}")
+ "})")
+ "ltN64" (str "(function LuxRT$ltN64(l,r) {"
+ "var li = LuxRT$addI64(l,LuxRT$MIN_VALUE_I64);"
+ "var ri = LuxRT$addI64(r,LuxRT$MIN_VALUE_I64);"
+ "return LuxRT$ltI64(li,ri);"
+ "})")
+ })
+
+(def ^:private d64-methods
+ {"mulD64" (str "(function LuxRT$mulD64(l,r) {"
+ "var lL = LuxRT$fromNumberI64(l.L);"
+ "var rL = LuxRT$fromNumberI64(r.L);"
+ "var lH = LuxRT$fromNumberI64(l.H);"
+ "var rH = LuxRT$fromNumberI64(r.H);"
+
+ "var bottom = LuxRT$ushrI64(LuxRT$mulI64(lL,rL),32);"
+ "var middle = LuxRT$addI64(LuxRT$mulI64(lH,rL),LuxRT$mulI64(lL,rH));"
+ "var top = LuxRT$mulI64(lH,rH);"
+
+ "var bottomAndMiddle = LuxRT$ushrI64(LuxRT$addI64(middle,bottom),32);"
+
+ "return LuxRT$addI64(top,bottomAndMiddle);"
+ "})")
+ "divD64" (str "(function LuxRT$divD64(l,r) {"
+ "return LuxRT$shlI64(LuxRT$divI64(l,LuxRT$fromNumberI64(r.H)),32);"
+ "})")
+ "degToReal" (str "(function LuxRT$degToReal(input) {"
+ "var two32 = Math.pow(2,32);"
+ "var high = input.H / two32;"
+ "var low = (input.L / two32) / two32;"
+ "return high+low;"
+ "})")
+ "realToDeg" (str "(function LuxRT$realToDeg(input) {"
+ "var two32 = Math.pow(2,32);"
+ "var shifted = (input % 1.0) * two32;"
+ "var low = ((shifted % 1.0) * two32) | 0;"
+ "var high = shifted | 0;"
+ "return LuxRT$makeI64(high,low);"
+ "})")
+ "_add_deg_digit_powers" (str "(function LuxRT$_add_deg_digit_powers(left,right) {"
+ "var output = new Array(64);"
+ "var carry = 0;"
+ (str "for(var idx = 63; idx >= 0; idx--) {"
+ "var raw = left[idx] + right[idx] + carry;"
+ "output[idx] = raw % 10;"
+ "raw = (raw / 10)|0;"
+ "}")
+ "return output;"
+ "})")
+ "_times5" (str "(function LuxRT$_times5(exp,digits) {"
+ "var carry = 0;"
+ (str "for(var idx = exp; idx >= 0; idx--) {"
+ "var raw = (digits[exp] * 5) + carry;"
+ "digits[exp] = raw % 10;"
+ "carry = (raw / 10)|0;"
+ "}")
+ "return digits;"
+ "})")
+ "_deg_digit_power" (str "(function LuxRT$_deg_digit_power(exp) {"
+ "var digits = new Array(64);"
+ "digits[exp] = 1;"
+ (str "for(var idx = exp; idx >= 0; idx--) {"
+ "digits = LuxRT$_times5(exp,digits);"
+ "}")
+ "return digits;"
+ "})")
+ "_bitIsSet" (str "(function LuxRT$_bitIsSet(input,idx) {"
+ "idx &= 63;"
+ (str "if(idx < 32) {"
+ "return (input.L & (1 << idx)) !== 0;"
+ "}")
+ (str "else {"
+ "return (input.H & (1 << (idx - 32))) !== 0;"
+ "}")
+ "})")
+ "encodeD64" (str "(function LuxRT$encodeD64(input) {"
+ (str "if(LuxRT$eqI64(input,LuxRT$ZERO)) {"
+ "return '.0';"
+ "}")
+ "var digits = new Array(64);"
+ (str "for(var idx = 63; idx >= 0; idx--) {"
+ (str "if(LuxRT$_bitIsSet(input,idx)) {"
+ "var power = LuxRT$_deg_digit_power(63 - idx);"
+ "digits = LuxRT$_add_deg_digit_powers(digits,power);"
+ "}")
+ "}")
+ "var raw = '.'.concat(digits.join(''));"
+ "return raw.split(/0*$/)[0];"
+ "})")
+ "deg_text_to_digits" (str "(function LuxRT$deg_text_to_digits(input) {"
+ "var output = new Array(64);"
+ (str "for(var idx = input.length-1; idx >= 0; idx--) {"
+ "output[idx] = parseInt(input.substring(idx, idx+1));"
+ "}")
+ "return output;"
+ "})")
+ "deg_digits_lt" (str "(function LuxRT$deg_digits_lt(l,r) {"
+ (str "for(var idx = 0; idx < 64; idx++) {"
+ (str "if(l[idx] < r[idx]) {"
+ "return true;"
+ "}"
+ "else if(l[idx] > r[idx]) {"
+ "return false;"
+ "}")
+ "}")
+ "return false;"
+ "})")
+ "deg_digits_sub_once" (str "(function LuxRT$deg_digits_sub_once(target,digit,idx) {"
+ (str "while(true) {"
+ (str "if(target[idx] > digit) {"
+ (str "target[idx] = target[idx] - digit;"
+ "return target;")
+ "}"
+ "else {"
+ (str "target[idx] = 10 - (digit - target[idx]);"
+ "idx--;"
+ "digit=1;")
+ "}")
+ "}")
+ "})")
+ "deg_digits_sub" (str "(function LuxRT$deg_digits_sub(l,r) {"
+ (str "for(var idx = 63; idx >= 0; idx--) {"
+ "l = LuxRT$deg_digits_sub_once(l,r[idx],idx);"
+ "}")
+ "return l;"
+ "})")
+ "decodeD64" (let [failure (str "return " const-none ";")]
+ (str "(function LuxRT$decodeD64(input) {"
+ "input = LuxRT$clean_separators(input);"
+ (str "if(/^\\.\\d+$/.exec(input) && input.length <= 65) {"
+ (str "try {"
+ (str "var digits = LuxRT$deg_text_to_digits(input.substring(1));")
+ "var output = LuxRT$makeI64(0,0);"
+ (str "for(var idx = 0; idx < 64; idx++) {"
+ "var power = LuxRT$deg_text_to_digits(idx);"
+ (str "if(LuxRT$deg_digits_lt(power,digits)) {"
+ (str "digits = LuxRT$deg_digits_sub(digits,power);"
+ "var powerBit = LuxRT$shlI64(LuxRT$makeI64(0,1),(63-idx));"
+ "output = LuxRT$orI64(output,powerBit);")
+ "}")
+ "}")
+ (str "return " (make-some "output") ";")
+ "}"
+ "catch(ex) {"
+ failure
+ "}")
+ "}"
+ "else {"
+ failure
+ "}")
+ "})"))
+ })
+
+(def ^:private io-methods
+ {"log" (str "(function LuxRT$log(message) {"
+ "console.log(message);"
+ (str "return " &&/unit ";")
+ "})")
+ "error" (str "(function LuxRT$error(message) {"
+ "throw new Error(message);"
+ (str "return null;")
+ "})")
+ })
+
+(def ^:private text-methods
+ {"index" (str "(function LuxRT$index(text,part,start) {"
+ "var idx = text.indexOf(part,LuxRT$toNumberI64(start));"
+ (str (str "if(idx === -1) {"
+ "return " const-none ";"
+ "}")
+ (str "else {"
+ (str "return " (make-some "LuxRT$fromNumberI64(idx)") ";")
+ "}"))
+ "})")
+ "lastIndex" (str "(function LuxRT$lastIndex(text,part,start) {"
+ "var idx = text.lastIndexOf(part,LuxRT$toNumberI64(start));"
+ (str (str "if(idx === -1) {"
+ "return " const-none ";"
+ "}")
+ (str "else {"
+ (str "return " (make-some "LuxRT$fromNumberI64(idx)") ";")
+ "}"))
+ "})")
+ "clip" (str "(function LuxRT$clip(text,from,to) {"
+ (str "if(from.L > text.length || to.L > text.length) {"
+ (str "return " const-none ";")
+ "}"
+ "else {"
+ (str "return " (make-some "text.substring(from.L,to.L)") ";")
+ "}")
+ "})")
+ "replaceAll" (str "(function LuxRT$replaceAll(text,toFind,replaceWith) {"
+ "var reEscaped = toFind.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');"
+ "return text.replace(new RegExp(reEscaped, 'g'), replaceWith);"
+ "})")
+ "textChar" (str "(function LuxRT$textChar(text,idx) {"
+ "var result = text.charAt(idx.L);"
+ (str "if(result === '') {"
+ (str "return " const-none ";")
+ "}"
+ "else {"
+ (str "return " (make-some "{'C':result}") ";")
+ "}")
+ "var reEscaped = toFind.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');"
+ "return text.replace(new RegExp(reEscaped, 'g'), replaceWith);"
+ "})")
+ "textHash" (str "(function LuxRT$textHash(input) {"
+ "var hash = 0;"
+ (str "for(var i = 0; i < input.length; i++) {"
+ "hash = (((hash << 5) - hash) + input.charCodeAt(i)) & 0xFFFFFFFF;"
+ "}")
+ "return LuxRT$fromNumberI64(hash);"
+ "})")
+ })
+
+(def ^:private array-methods
+ {"arrayGet" (str "(function LuxRT$arrayGet(arr,idx) {"
+ "var temp = arr[LuxRT$toNumberI64(idx)];"
+ (str "if(temp !== undefined) {"
+ (str "return " (make-some "temp") ";")
+ "}"
+ "else {"
+ (str "return " const-none ";")
+ "}")
+ "})")
+ "arrayPut" (str "(function LuxRT$arrayPut(arr,idx,val) {"
+ "arr[LuxRT$toNumberI64(idx)] = val;"
+ "return arr;"
+ "})")
+ "arrayRemove" (str "(function LuxRT$arrayRemove(arr,idx) {"
+ "delete arr[LuxRT$toNumberI64(idx)];"
+ "return arr;"
+ "})")
+ })
+
+(def ^:private bit-methods
+ (let [make-basic-op (fn [op name]
+ (str "(function " name "(input,mask) {"
+ "return LuxRT$makeI64(input.H " op " mask.H, input.L " op " mask.L);"
+ "})"))]
+ {"andI64" (make-basic-op "&" "LuxRT$andI64")
+ "orI64" (make-basic-op "|" "LuxRT$orI64")
+ "xorI64" (make-basic-op "^" "LuxRT$xorI64")
+ "countI64" (str "(function LuxRT$countI64(input) {"
+ "var hs = (input.H).toString(2);"
+ "var ls = (input.L).toString(2);"
+ "var num1s = hs.concat(ls).replace(/0/g,'').length;"
+ "return LuxRT$fromNumberI64(num1s);"
+ "})")
+ "shlI64" (str "(function LuxRT$shlI64(input,shift) {"
+ "shift &= 63;"
+ (str "if(shift === 0) {"
+ "return input;"
+ "}"
+ "else {"
+ (str "if (shift < 32) {"
+ "var high = (input.H << shift) | (input.L >>> (32 - shift));"
+ "var low = input.L << shift;"
+ "return LuxRT$makeI64(high, low);"
+ "}"
+ "else {"
+ "var high = (input.L << (shift - 32));"
+ "return LuxRT$makeI64(high, 0);"
+ "}")
+ "}")
+ "})")
+ "shrI64" (str "(function LuxRT$shrI64(input,shift) {"
+ "shift &= 63;"
+ (str "if(shift === 0) {"
+ "return input;"
+ "}"
+ "else {"
+ (str "if (shift < 32) {"
+ "var high = input.H >> shift;"
+ "var low = (input.L >>> shift) | (input.H << (32 - shift));"
+ "return LuxRT$makeI64(high, low);"
+ "}"
+ "else {"
+ "var low = (input.H >> (shift - 32));"
+ "var high = input.H >= 0 ? 0 : -1;"
+ "return LuxRT$makeI64(high, low);"
+ "}")
+ "}")
+ "})")
+ "ushrI64" (str "(function LuxRT$ushrI64(input,shift) {"
+ "shift &= 63;"
+ (str "if(shift === 0) {"
+ "return input;"
+ "}"
+ "else {"
+ (str "if (shift < 32) {"
+ "var high = input.H >>> shift;"
+ "var low = (input.L >>> shift) | (input.H << (32 - shift));"
+ "return LuxRT$makeI64(high, low);"
+ "}"
+ "else if(shift === 32) {"
+ "return LuxRT$makeI64(0, input.H);"
+ "}"
+ "else {"
+ "var low = (input.H >>> (shift - 32));"
+ "return LuxRT$makeI64(0, low);"
+ "}")
+ "}")
+ "})")
+ }))
+
+(def ^:private lux-methods
+ {"clean_separators" (str "(function LuxRT$clean_separators(input) {"
+ "return input.replace(/_/g,'');"
+ "})")
+ "runTry" (str "(function LuxRT$runTry(op) {"
+ (str "try {"
+ (str "return [1,'',op(null)];")
+ "}"
+ "catch(ex) {"
+ (str "return [0,null,ex.toString()];")
+ "}")
+ "})")
+ "programArgs" (str "(function LuxRT$programArgs() {"
+ (str "if(typeof process !== 'undefined' && process.argv) {"
+ (str (str "var result = " const-none ";")
+ "for(var idx = process.argv.length-1; idx >= 0; idx--) {"
+ (str "result = " (make-some "[process.argv[idx],result]") ";")
+ "}")
+ (str "return result;")
+ "}"
+ "else {"
+ (str "return " const-none ";")
+ "}")
+ "})")
+ })
+
+(def ^:private js-methods
+ {"jsSetField" (str "(function LuxRT$jsSetField(object, field, input) {"
+ "object[field] = input;"
+ "return object;"
+ "})")
+ "jsDeleteField" (str "(function LuxRT$jsDeleteField(object, field) {"
+ "delete object[field];"
+ "return object;"
+ "})")
+ "jsObjectCall" (str "(function LuxRT$jsObjectCall(object, method, args) {"
+ "return object[method].apply(object, args);"
+ "})")
+ })
+
+(def LuxRT "LuxRT")
+
+(def compile-LuxRT
+ (&&/save-js! LuxRT
+ (->> (merge lux-methods
+ adt-methods
+ i64-methods
+ n64-methods
+ d64-methods
+ text-methods
+ array-methods
+ bit-methods
+ io-methods
+ js-methods)
+ (reduce (fn [prev [key val]] (str prev "var LuxRT$" key " = " val ";\n"))
+ ""))))