-
Notifications
You must be signed in to change notification settings - Fork 3
/
inline-op.lisp
94 lines (82 loc) · 3.22 KB
/
inline-op.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
(in-package :cl-js)
(defgeneric expand-op (op lht rht lhs rhs)
(:method (op lht rht lhs rhs)
(declare (ignore op lht rht lhs rhs))
nil))
(defmacro defexpand (op (lht rht) &body body)
(flet ((spec (type)
(if (eq type t) t `(eql ,type))))
`(defmethod expand-op ((,(gensym) (eql ,op)) (lht ,(spec lht))
(rht ,(spec rht)) lhs rhs)
(declare (ignorable lhs rhs))
,@body)))
(defun expand (op lht rht lhs rhs)
(let ((result (expand-op op lht rht lhs rhs)))
(when (and (not result) (eq lht :integer))
(setf result (expand-op op :number rht lhs rhs)))
(when (and (not result) (eq rht :integer))
(setf result (expand-op op (if (eq lht :integer) :number lht) :number lhs rhs)))
result))
(defun to-boolean-typed (expr type)
(case type
(:boolean expr)
((:undefined :null) `(progn ,expr nil))
(:object `(progn ,expr t))
(:integer `(not (= ,expr 0)))
(:number (let ((tmp (gensym))) `(let ((,tmp ,expr)) (not (or (= ,tmp 0) (is-nan ,tmp))))))
(t `(to-boolean ,expr))))
(defmacro defnumop (op expansion)
`(progn (defexpand ,op (:integer :integer) ,expansion)
(defexpand ,op (:number :number) (unless *float-traps* ,expansion))))
;; (string + string is handled specially in the :binary translate rule)
(defnumop :+ `(+ ,lhs ,rhs))
(defexpand :+ (nil :number) rhs)
(defexpand :+ (t :number)
(unless *float-traps*
(let ((lh (gensym)) (rh (gensym)))
`(let ((,lh ,lhs) (,rh ,rhs))
(typecase ,lh
(fixnum (+ (the fixnum ,lh) ,rh))
(double-float (+ (the double-float ,lh) ,rh))
(t (js+ ,lh ,rh)))))))
(defexpand :+ (:number t)
(unless *float-traps*
(let ((lh (gensym)) (rh (gensym)))
`(let ((,lh ,lhs) (,rh ,rhs))
(typecase ,rh
(fixnum (+ ,lh (the fixnum ,rh)))
(double-float (+ ,lh (the double-float ,rh)))
(t (js+ ,lh ,rh)))))))
(defnumop :- `(- ,lhs ,rhs))
(defexpand :- (nil t)
(let ((val (gensym)))
`(let ((,val (to-number ,rhs)))
(if (zerop ,val) (- 0d0) (js- 0 ,val)))))
(defnumop :* `(* ,lhs ,rhs))
(defnumop :% `(rem ,lhs ,rhs))
(defnumop :< `(< ,lhs ,rhs))
(defnumop :> `(> ,lhs ,rhs))
(defnumop :<= `(<= ,lhs ,rhs))
(defnumop :>= `(>= ,lhs ,rhs))
(defexpand :== (:integer :integer) `(= ,lhs ,rhs))
(defnumop :!= `(/= ,lhs ,rhs))
(defnumop :=== `(= ,lhs ,rhs))
(defnumop :!== `(/= ,lhs ,rhs))
(defexpand :& (:integer :integer) `(logand (trunc32 ,lhs) (trunc32 ,rhs)))
(defexpand :|\|| (:integer :integer) `(logior (trunc32 ,lhs) (trunc32 ,rhs)))
(defexpand :^ (:integer :integer) `(logxor (trunc32 ,lhs) (trunc32 ,rhs)))
(defexpand :~ (nil :integer) `(lognot (trunc32 ,rhs)))
(defexpand :>> (:integer :integer) `(ash (trunc32 ,lhs) (- ,rhs)))
(defexpand :<< (:integer :integer) `(ash (trunc32 ,lhs) ,rhs))
(defexpand :>>> (:integer :integer) `(bitshift32 (trunc32 ,lhs) (- ,rhs)))
(defexpand :&& (t t)
(let ((temp (gensym)))
`(let ((,temp ,lhs))
(if ,(to-boolean-typed temp lht) ,rhs ,temp))))
(defexpand :|\|\|| (t t)
(let ((temp (gensym)))
`(let ((,temp ,lhs))
(if ,(to-boolean-typed temp lht) ,temp ,rhs))))
(defexpand :! (t t) `(not ,(to-boolean-typed rhs rht)))
(defexpand :void (t t)
`(progn ,rhs :undefined))