-
Notifications
You must be signed in to change notification settings - Fork 71
/
Copy pathlanguage-macros.lisp
167 lines (121 loc) · 5.39 KB
/
language-macros.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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
(in-package #:coalton)
;;;; Macros used to implement the Coalton language
(cl:defmacro as (type cl:&optional (expr cl:nil expr-supplied-p))
"A syntactic convenience for type casting.
(as <type> <expr>)
is equivalent to
(the <type> (into <expr>))
and
(as <type>)
is equivalent to
(fn (expr) (the <type> (into expr))).
Note that this may copy the object or allocate memory."
(cl:let ((into (cl:ignore-errors (cl:find-symbol "INTO" "COALTON-LIBRARY/CLASSES"))))
(cl:assert into () "`as` macro does not have access to `into` yet.")
(cl:if expr-supplied-p
`(the ,type (,into ,expr))
(alexandria:with-gensyms (lexpr)
`(fn (,lexpr)
(the ,type (,into ,lexpr)))))))
(cl:defmacro try-as (type cl:&optional (expr cl:nil expr-supplied-p))
"A syntactic convenience for type casting.
(try-as <type> <expr>)
is equivalent to
(the (Result :_ <type>) (tryInto <expr>))
and
(try-as <type>)
is equivalent to
(fn (expr) (the (Result :_ <type>) (tryInto expr))).
Note that this may copy the object or allocate memory."
(cl:let ((try-into (cl:ignore-errors (cl:find-symbol "TRYINTO" "COALTON-LIBRARY/CLASSES")))
(Result (cl:ignore-errors (cl:find-symbol "RESULT" "COALTON-LIBRARY/CLASSES"))))
(cl:assert try-into () "`try-as` macro does not have access to `try-into` yet.")
(cl:assert Result () "`try-as` macro does not have access to `Result` yet.")
(cl:if expr-supplied-p
`(the (,Result :_ ,type) (,try-into ,expr))
(alexandria:with-gensyms (lexpr)
`(fn (,lexpr)
(the (,Result :_ ,type) (,try-into ,lexpr)))))))
(cl:defmacro unwrap-as (type cl:&optional (expr cl:nil expr-supplied-p))
"A syntactic convenience for type casting.
(unwrap-as <type> <expr>)
is equivalent to
(the <type> (uwrap (tryInto <expr>)))
and
(unwrap-as <type>)
is equivalent to
(fn (expr) (the <type> (unwrap (tryInto expr)))).
Note that this may copy the object or allocate memory."
(cl:let ((try-into (cl:ignore-errors (cl:find-symbol "TRYINTO" "COALTON-LIBRARY/CLASSES")))
(unwrap (cl:ignore-errors (cl:find-symbol "UNWRAP" "COALTON-LIBRARY/CLASSES"))))
(cl:assert try-into () "`try-as` macro does not have access to `try-into` yet.")
(cl:assert unwrap () "`unwrap` macro does not have access to `unwrap` yet.")
(cl:if expr-supplied-p
`(the ,type (,unwrap (,try-into ,expr)))
(alexandria:with-gensyms (lexpr)
`(fn (,lexpr)
(the ,type (,unwrap (,try-into ,lexpr))))))))
(cl:defmacro nest (cl:&rest items)
"A syntactic convenience for function application. Transform
(NEST f g h x)
to
(f (g (h x)))."
(cl:assert (cl:<= 2 (cl:list-length items)))
(cl:let ((last (cl:last items))
(butlast (cl:butlast items)))
(cl:reduce (cl:lambda (x acc)
(cl:list x acc))
butlast :from-end cl:t :initial-value (cl:first last))))
(cl:defmacro pipe (cl:&rest items)
"A syntactic convenience for function application, sometimes called a \"threading macro\". Transform
(PIPE x h g f)
to
(f (g (h x)))."
(cl:assert (cl:<= 2 (cl:list-length items)))
`(nest ,@(cl:reverse items)))
(cl:defmacro .< (cl:&rest items)
"Right associative compose operator. Creates a new functions that will run the
functions right to left when applied. This is the same as the NEST macro without supplying
the value. The composition is thus the same order as COMPOSE.
`(.< f g h)` creates the function `(fn (x) (f (g (h x))))"
(alexandria:with-gensyms (x)
`(fn (,x)
(nest ,@items ,x))))
(cl:defmacro .> (cl:&rest items)
"Left associative compose operator. Creates a new functions that will run the
functions left to right when applied. This is the same as the PIPE macro without supplying
the value. The composition is thus the reverse order of COMPOSE.
`(.> f g h)` creates the function `(fn (x) (h (g (f x))))"
(alexandria:with-gensyms (x)
`(fn (,x)
(pipe ,x ,@items))))
(cl:defmacro make-list (cl:&rest forms)
(cl:labels
((list-helper (forms)
(cl:if (cl:endp forms)
`coalton:Nil
`(coalton:Cons ,(cl:car forms) ,(list-helper (cl:cdr forms))))))
(list-helper forms)))
(cl:defmacro to-boolean (expr)
"Convert the Lisp expression EXPR, representing a generalized boolean, to a
Coalton boolean."
`(cl:and ,expr cl:t))
(cl:defmacro assert (datum cl:&optional (format-string "") cl:&rest format-data)
"Signal an error unless DATUM is `True'.
If the assertion fails, the signaled error will apply the FORMAT-DATA to the FORMAT-STRING via `cl:format' to
produce an error message."
;; OPTIMIZE: lazily evaluate the FORMAT-DATA only when the assertion fails
(cl:check-type format-string cl:string)
(cl:let* ((datum-temp (cl:gensym "ASSERT-DATUM-"))
(format-data-temps (alexandria:make-gensym-list (cl:length format-data)
"ASSERT-FORMAT-DATUM-")))
`(let ((,datum-temp ,datum)
,@(cl:mapcar #'cl:list format-data-temps format-data))
(progn
(lisp :any (,datum-temp ,@format-data-temps)
(cl:assert ,datum-temp ()
,(cl:format cl:nil
"Assertion ~a failed: ~a"
datum format-string)
,@format-data-temps))
Unit))))