(module standard-library mzscheme
(require (planet "evector.scm" ("soegaard" "evector.plt" 1 0))
(only (lib "mred.ss" "mred") message-box)
(lib "string.ss" "srfi" "13")
(lib "list.ss" "srfi" "1")
(lib "match.ss")
(lib "math.ss")
(lib "etc.ss")
"../debug.ss"
"../syntax/regexps.ss"
"eval.ss"
"exceptions.ss"
"value.ss")
(define (object-descriptor object)
(format "[object ~a]" (object-class object)))
(define js:print
(build-function 1
(lambda (args)
(let ([print1 (lambda (x)
(display (value->string x)))]
[args (evector->list args)])
(unless (null? args)
(print1 (car args))
(for-each (lambda (arg)
(display " ")
(print1 arg))
(cdr args)))
(newline)))))
(define js:alert
(build-function 1
(lambda (args)
(let ([args (evector->list args)])
(when (null? args)
(raise-runtime-exception here "not enough arguments"))
(let* ([msg (value->string (car args))]
[msg-padded (if (< (string-length msg) 20)
(string-pad-right msg 20 #\space)
msg)])
(message-box "JavaScript" msg-padded #f '(ok)))
(void)))))
(define js:parseInt
(build-function 2
(lambda (args)
(let ([string (if (>= (evector-length args) 1)
(evector-ref args 0)
(void))]
[radix (if (>= (evector-length args) 2)
(evector-ref args 1)
(void))])
(let* ([s (string-trim (value->string string) char-whitespace?)]
[r (value->int32 radix)]
[sign (if (char=? (string-ref s 0) #\-)
(begin (set! s (substring s 1)) -1)
1)])
(if (or (and (not (zero? r)) (< r 2))
(> r 36))
+nan.0
(let ([r (cond
[(or (string-prefix? "0x" s) (string-prefix? "0X" s))
(set! s (substring s 2))
16]
[(string-prefix? "0" s)
(set! s (substring s 1))
8]
[(zero? r)
10]
[else r])])
(cond
[(regexp-match (build-integer-regexp r) s)
=> (lambda (match)
(let sum ([factor 1]
[total 0]
[digits (map char->digit (reverse (string->list (car match))))])
(if (null? digits)
total
(sum (* factor r)
(+ total (* (car digits) factor))
(cdr digits)))))]
[else +nan.0]))))))))
(define (char->digit ch)
(cond
[(memv ch (string->list "0123456789"))
(- (char->integer ch) (char->integer #\0))]
[(memv ch (string->list "ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
(- (char->integer ch) (char->integer #\A))]
[(memv ch (string->list "abcdefghijklmnopqrstuvwxyz"))
(- (char->integer ch) (char->integer #\a))]
[else
(error 'char->digit "bad digit: ~a" ch)]))
(define (build-integer-regexp base)
(regexp
(cond
[(<= base 10)
(format "^[0-~a]+" (sub1 base))]
[(= base 11)
"^[0-9Aa]+"]
[else
(let ([last-char-index (- base 11)])
(format "^[0-9A-~aa-~a]+"
(string-ref "ABCDEFGHIJKLMNOPQRSTUVWXYZ" last-char-index)
(string-ref "abcdefghijklmnopqrstuvwxyz" last-char-index)))])))
(define js:parseFloat
(build-function 1
(lambda (args)
(let ([s (string-trim (value->string (if (> (evector-length args) 0)
(evector-ref args 0)
(void)))
char-whitespace?)])
(cond
[(regexp-match rx:float s)
=> (lambda (match)
(string->number (car match)))]
[else +nan.0])))))
(define js:isNaN
(build-function 1
(lambda (args)
(make-boolean (NaN? (value->number (if (> (evector-length args) 0)
(evector-ref args 0)
(void))))))))
(define js:isFinite
(build-function 1
(lambda (args)
(let ([x (value->number (if (> (evector-length args) 0)
(evector-ref args 0)
(void)))])
(cond
[(NaN? x) 'false]
[(infinite? x) 'false]
[else 'true])))))
(define js:eval
(build-function 1
(lambda (args)
(if (zero? (evector-length args))
(void)
(eval-javascript-string (value->string (evector-ref args 0))
(current-namespace))))))
(define (tmp:stub arity name)
(build-function arity
(lambda (args)
(error name "not yet implemented"))))
(define js:decodeURI (tmp:stub 1 'decodeURI))
(define js:decodeURIComponent (tmp:stub 1 'decodeURIComponent))
(define js:encodeURI (tmp:stub 1 'encodeURI))
(define js:encodeURIComponent (tmp:stub 1 'encodeURIComponent))
(define (new-Object arg-vec)
(if (or (zero? (evector-length arg-vec))
(null? (evector-ref arg-vec 0))
(void? (evector-ref arg-vec 0)))
(make-object #f #f proto:Object "Object" (object-table))
(value->object (evector-ref arg-vec 0))))
(define (new-Function arg-vec)
(let ([args (evector->list arg-vec)])
(cond
[(null? args) (build-function 0 void)]
[(null? (cdr args))
(eval-function-string (format "function(){~a}" (value->string (car args)))
(current-namespace))]
[else
(eval-function-string (format "function(~a){~a}"
(string-join (map value->string (drop-right args 1)) "," 'infix)
(value->string (last args)))
(current-namespace))])))
(define (new-Array arg-vec)
(let ([len (evector-length arg-vec)])
(if (= len 1)
(new-Array1 (evector-ref arg-vec 0))
(build-array arg-vec))))
(define (new-Array1 len)
(if (numeric? len)
(let* ([val (numeric->number len)]
[uint32 (value->uint32 val)])
(if (= val uint32)
(let ([a (build-array (evector))])
(set-array-length! a uint32)
a)
(build-array (evector len))))
(build-array (evector len))))
(define (new-String arg-vec)
(let* ([value (if (zero? (evector-length arg-vec))
""
(value->string (evector-ref arg-vec 0)))]
[table (object-table)])
(hash-table-put! table '<<value>> value)
(make-object #f #f proto:String "String" table)))
(define (new-Boolean arg-vec)
(let* ([value (if (zero? (evector-length arg-vec))
'false
(value->boolean (evector-ref arg-vec 0)))]
[table (object-table)])
(hash-table-put! table '<<value>> value)
(make-object #f #f proto:Boolean "Boolean" table)))
(define (new-Number arg-vec)
(let* ([value (if (zero? (evector-length arg-vec))
0
(value->number (evector-ref arg-vec 0)))]
[table (object-table)])
(hash-table-put! table '<<value>> value)
(make-object #f #f proto:Number "Number" table)))
(define (reset-object! object)
(set-object-properties! object (object-table)))
(define (reset-global-object! global)
(reset-object! global)
(reset-object! proto:global) (reset-object! proto:proto)
(object-put! proto:global "toString" (build-function 0
(lambda (arg-vec)
(object-descriptor (current-this)))))
(object-put! proto:global "hasOwnProperty" (build-function 1
(lambda (arg-vec)
(make-boolean
(has-property?/immediate (current-this)
(get-arg arg-vec 0)))))))
(define (reset-primitive-constructors! global)
(for-each (lambda (ctor proto name call construct)
(reset-object! proto)
(reset-object! ctor)
(set-object-call! ctor call)
(set-object-construct! ctor construct)
(object-put! ctor "prototype" proto (bit-field DONT-ENUM? DONT-DELETE? READ-ONLY?))
(object-put! ctor "length" 1 (bit-field DONT-ENUM? DONT-DELETE? READ-ONLY?))
(object-put! global name ctor (bit-field DONT-ENUM? DONT-DELETE?))
(object-put! proto "constructor" ctor))
(list Object Function Array String Boolean Number)
(list proto:Object proto:Function proto:Array proto:String proto:Boolean proto:Number)
(list "Object" "Function" "Array" "String" "Boolean" "Number")
(list
(lambda (arg-vec)
(if (or (zero? (evector-length arg-vec))
(null? (evector-ref arg-vec 0))
(void? (evector-ref arg-vec 0)))
(new-Object arg-vec)
(value->object (evector-ref arg-vec 0))))
new-Function
new-Array
(compose value->string get-arg0)
(compose value->boolean get-arg0)
(compose value->number get-arg0))
(list new-Object new-Function new-Array new-String new-Boolean new-Number))
(reset-object! Math)
(object-put! global "Math" Math (bit-field DONT-ENUM? DONT-DELETE?)))
(define Object-methods
`( (toString ,(build-function 0
(lambda (arg-vec)
(object-descriptor (current-this)))))
(toLocaleString ,(build-function 0
(lambda (arg-vec)
(let ([toString (object-get (current-this) "toString")])
(if (not toString)
(raise-runtime-type-error here "function" "undefined")
(call toString arg-vec (lambda (s1 s2)
(raise-runtime-type-error here s1 s2))))))))
(valueOf ,(build-function 0
(lambda (arg-vec)
(current-this))))
(hasOwnProperty ,(build-function 0
(lambda (arg-vec)
(make-boolean
(has-property?/immediate (current-this)
(get-arg arg-vec 0))))))
(isPrototypeOf ,(tmp:stub 1 "isPrototypeOf"))
(propertyIsEnumerable ,(tmp:stub 1 "propertyIsEnumerable"))
))
(define Function-methods
`( (toString ,(build-function 0
(lambda (arg-vec)
(unless (descendant-of? (current-this) proto:Function)
(raise-runtime-type-error here "function" "object"))
"[object Function]")))
(apply ,(tmp:stub 2 "apply"))
(call ,(tmp:stub 1 "call"))
))
(define Array-methods
`( (toString ,(build-function 0
(lambda (arg-vec)
(let ([this (current-this)])
(string-join (map (lambda (elt)
(if elt (value->string elt) ""))
(evector->list (array-vector this)))
","
'infix)))))
(toLocaleString ,(build-function 0
(lambda (arg-vec)
(let ([this (current-this)])
(unless (descendant-of? this proto:Array)
(raise-runtime-type-error here "array" "object"))
(string-join (map (lambda (elt)
(if elt
(invoke elt "toLocaleString" (evector) (lambda (s1 s2)
(raise-runtime-type-error here s1 s2)))
""))
(evector->list (array-vector this)))
","
'infix)))))
(concat ,(tmp:stub 1 "concat"))
(join ,(tmp:stub 1 "join"))
(pop ,(tmp:stub 0 "pop"))
(push ,(tmp:stub 1 "push"))
(reverse ,(tmp:stub 0 "reverse"))
(shift ,(tmp:stub 0 "shift"))
(slice ,(tmp:stub 2 "slice"))
(sort ,(tmp:stub 1 "sort"))
(splice ,(tmp:stub 2 "splice"))
(unshift ,(tmp:stub 1 "unshift"))
))
(define String-statics
`( (fromCharCode ,(build-function 1
(lambda (arg-vec)
(list->string
(map (compose integer->char value->uint16)
(evector->list arg-vec))))))
))
(define String-methods
`( (toString ,(build-function 0
(lambda (arg-vec)
(current-this))))
(valueOf ,(tmp:stub 0 "valueOf"))
(charAt ,(tmp:stub 1 "charAt"))
(charCodeAt ,(tmp:stub 1 "charCodeAt"))
(concat ,(tmp:stub 1 "concat"))
(indexOf ,(tmp:stub 1 "indexOf"))
(lastIndexOf ,(tmp:stub 1 "lastIndexOf"))
(localeCompare ,(tmp:stub 1 "localeCompare"))
(match ,(tmp:stub 1 "match"))
(replace ,(tmp:stub 2 "replace"))
(search ,(tmp:stub 1 "search"))
(slice ,(tmp:stub 2 "slice"))
(split ,(tmp:stub 2 "split"))
(substring ,(tmp:stub 2 "substring"))
(toLowerCase ,(tmp:stub 0 "toLowerCase"))
(toLocaleLowerCase ,(tmp:stub 0 "toLocaleLowerCase"))
(toUpperCase ,(tmp:stub 0 "toUpperCase"))
(toLocaleUpperCase ,(tmp:stub 0 "toLocaleUpperCase"))
))
(define Boolean-methods
`( (toString ,(tmp:stub 0 "toString"))
(valueOf ,(tmp:stub 0 "valueOf"))
))
(define Number-statics
`( (MAX_VALUE ,(void) ,(bit-field DONT-DELETE? READ-ONLY? DONT-ENUM?))
(MIN_VALUE ,(void) ,(bit-field DONT-DELETE? READ-ONLY? DONT-ENUM?))
(NaN +nan.0 ,(bit-field DONT-DELETE? READ-ONLY? DONT-ENUM?))
(NEGATIVE_INFINITY -inf.0 ,(bit-field DONT-DELETE? READ-ONLY? DONT-ENUM?))
(POSITIVE_INFINITY +inf.0 ,(bit-field DONT-DELETE? READ-ONLY? DONT-ENUM?))
))
(define Number-methods
`( (toString ,(tmp:stub 0 "toString"))
(toLocaleString ,(tmp:stub 0 "toLocaleString"))
(valueOf ,(tmp:stub 0 "valueOf"))
(toFixed ,(tmp:stub 1 "toFixed"))
(toExponential ,(tmp:stub 1 "toExponential"))
(toPrecision ,(tmp:stub 1 "toPrecision"))
))
(define Math-static-properties
`( (E ,e ,(bit-field DONT-ENUM? DONT-DELETE? READ-ONLY?))
(LN10 ,(log 10) ,(bit-field DONT-ENUM? DONT-DELETE? READ-ONLY?))
(LN2 ,(log 2) ,(bit-field DONT-ENUM? DONT-DELETE? READ-ONLY?))
(LOG2E ,(/ 1 (log 2)) ,(bit-field DONT-ENUM? DONT-DELETE? READ-ONLY?))
(LOG10E ,(/ 1 (log 10)) ,(bit-field DONT-ENUM? DONT-DELETE? READ-ONLY?))
(PI ,pi ,(bit-field DONT-ENUM? DONT-DELETE? READ-ONLY?))
(SQRT1_2 ,(sqrt 1/2) ,(bit-field DONT-ENUM? DONT-DELETE? READ-ONLY?))
(SQRT_2 ,(sqrt 2) ,(bit-field DONT-ENUM? DONT-DELETE? READ-ONLY?))
))
(define Math-static-methods
`( (abs ,(tmp:stub 1 "abs"))
(acos ,(tmp:stub 1 "acos"))
(asin ,(tmp:stub 1 "asin"))
(atan ,(tmp:stub 1 "atan"))
(atan2 ,(tmp:stub 2 "atan2"))
(ceil ,(tmp:stub 1 "ceil"))
(cos ,(tmp:stub 1 "cos"))
(exp ,(tmp:stub 1 "exp"))
(floor ,(tmp:stub 1 "floor"))
(log ,(tmp:stub 1 "log"))
(max ,(tmp:stub 2 "max"))
(min ,(tmp:stub 2 "min"))
(pow ,(tmp:stub 2 "pow"))
(random ,(tmp:stub 0 "random"))
(round ,(tmp:stub 1 "round"))
(sin ,(tmp:stub 1 "sin"))
(sqrt ,(tmp:stub 1 "sqrt"))
(tan ,(tmp:stub 1 "tan"))
))
(define global-properties
`( (NaN +nan.0 ,(bit-field DONT-ENUM? DONT-DELETE?))
(Infinity +inf.0 ,(bit-field DONT-ENUM? DONT-DELETE?))
(undefined ,(void) ,(bit-field DONT-ENUM? DONT-DELETE?))
))
(define global-methods
`( (eval ,js:eval)
(parseInt ,js:parseInt)
(parseFloat ,js:parseFloat)
(isNaN ,js:isNaN)
(isFinite ,js:isFinite)
(decodeURI ,js:decodeURI)
(decodeURIComponent ,js:decodeURIComponent)
(encodeURI ,js:encodeURI)
(encodeURIComponent ,js:encodeURIComponent)
))
(define global-custom-properties
`((it ,(void) ,(bit-field DONT-ENUM? DONT-DELETE?))
))
(define global-custom-methods
`( (print ,js:print)
(alert ,js:alert)
))
(define (install-properties! object properties)
(for-each (lambda (property)
(match property
[(name value)
(object-put! object (symbol->string name) value (bit-field DONT-ENUM?))]
[(name value attributes)
(object-put! object (symbol->string name) value attributes)]))
properties))
(define (install-standard-library! global)
(reset-global-object! global)
(reset-primitive-constructors! global)
(install-properties! global global-properties)
(install-properties! global global-methods)
(install-properties! global global-custom-properties)
(install-properties! global global-custom-methods)
(install-properties! proto:Object Object-methods)
(install-properties! proto:Function Function-methods)
(install-properties! proto:Array Array-methods)
(install-properties! String String-statics)
(install-properties! proto:String String-methods)
(install-properties! proto:Boolean Boolean-methods)
(install-properties! Number Number-statics)
(install-properties! proto:Number Number-methods)
(install-properties! Math Math-static-properties)
(install-properties! Math Math-static-methods)
(current-this global)
global)
(provide install-standard-library! reset-global-object! reset-primitive-constructors!))