(module operator mzscheme
(require (lib "string.ss" "srfi" "13")
"../syntax/ast.ss"
"exceptions.ss"
"value.ss")
(define (js:void v)
(void))
(define (js:delete ref)
(if (ref? ref)
(delete-ref! ref)
'true))
(define (js:typeof v)
(cond
[(void? v) "undefined"]
[(null? v) "object"]
[(symbol? v) "boolean"]
[(string? v) "string"]
[(function? v) "function"]
[(object? v) "object"]))
(define (js:instanceof x y)
(make-boolean (descendant-of? x y)))
(define (js:* x y)
(* (value->number x)
(value->number y)))
(define (js:/ x y)
(let ([result (exact->inexact (/ (value->number x)
(value->number y)))])
(if (integer? result)
(inexact->exact result)
result)))
(define js:+
(case-lambda
[(x) (value->number x)]
[(x y)
(let ([x (value->primitive x object->number)]
[y (value->primitive y object->number)])
(if (or (string? x) (string? y))
(string-append (value->string x)
(value->string y))
(+ (value->number x)
(value->number y))))]))
(define js:-
(case-lambda
[(x) (- (value->number x))]
[(x y) (- (value->number x)
(value->number y))]))
(define (js:% x y)
(let ([dividend (value->number x)]
[divisor (value->number y)])
(cond
[(or (NaN? dividend) (NaN? divisor) (infinite? dividend) (zero? divisor))
NaN]
[(and (not (infinite? dividend)) (infinite? divisor))
x]
[else
(let* ([quotient (exact->inexact (/ dividend divisor))]
[sign (if (negative? quotient) - +)]
[magnitude (inexact->exact (floor (abs quotient)))])
(- dividend (* divisor (sign magnitude))))])))
(define (js:<< x y)
(arithmetic-shift (value->int32 x)
(bitwise-and (value->uint32 y) #x1F)))
(define (js:>> x y)
(arithmetic-shift (value->int32 x)
(- (bitwise-and (value->uint32 y) #x1F))))
(define (js:>>> x y)
(arithmetic-shift (value->uint32 x)
(- (bitwise-and (value->uint32 y) #x1F))))
(define (js:& x y)
(bitwise-and (value->int32 x)
(value->int32 y)))
(define (js:^ x y)
(bitwise-xor (value->int32 x)
(value->int32 y)))
(define (js:\| x y)
(bitwise-ior (value->int32 x)
(value->int32 y)))
(define (js:~ x)
(bitwise-not (value->int32 x)))
(define (js:! x)
(make-boolean (eq? (value->boolean x) 'false)))
(define (less-than? x y)
(let ([x (value->primitive x object->number)]
[y (value->primitive y object->number)])
(if (and (string? x) (string? y))
(and (not (string-prefix? y x))
(string-prefix? x y))
(< (value->number x)
(value->number y)))))
(define (js:< x y)
(make-boolean (less-than? x y)))
(define (js:> x y)
(make-boolean (less-than? y x)))
(define (js:<= x y)
(make-boolean (not (less-than? y x))))
(define (js:>= x y)
(make-boolean (not (less-than? x y))))
(define (js:in x y)
(unless (object? y)
(raise-runtime-type-error here "object" (value->string y)))
(make-boolean (has-property? y (value->string x))))
(define (same-type? x y)
(or (and (void? x) (void? y))
(and (null? x) (null? y))
(and (symbol? x) (symbol? y))
(and (number? x) (number? y))
(and (string? x) (string? y))
(and (function? x) (function? y))
(and (object? x) (object? y))))
(define (== x y)
(cond
[(not (same-type? x y))
(or (and (null? x) (void? y))
(and (void? x) (null? y))
(and (number? x) (string? y) (== x (value->number y)))
(and (string? x) (number? y) (== (value->number x) y))
(and (symbol? x) (== (value->number x) y))
(and (symbol? y) (== x (value->number y)))
(and (or (string? x) (number? x)) (object? y) (== x (value->primitive y)))
(and (object? x) (or (string? y) (number? y)) (== (value->primitive x) y)))]
[(void? x) #t]
[(null? x) #t]
[(number? x) (= x y)]
[(string? x) (string=? x y)]
[(symbol? x) (eq? x y)]
[(object? x) (eq? x y)]))
(define (js:== x y)
(make-boolean (== x y)))
(define (js:!= x y)
(make-boolean (not (== x y))))
(define (=== x y)
(and (same-type? x y)
(or (void? x)
(null? x)
(and (number? x) (= x y))
(and (string? x) (string=? x y))
(and (symbol? x) (eq? x y))
(and (object? x) (eq? x y)))))
(define (js:=== x y)
(make-boolean (=== x y)))
(define (js:!== x y)
(make-boolean (not (=== x y))))
(define (operator->syntax op)
(if (or (infix-operator? op) (prefix-operator? op))
(datum->syntax-object #'js:in (string->symbol (format "js:~a" op)))
(error 'operator->syntax "bad operator")))
(provide (all-defined-except less-than? same-type? == ===)))