git.m455.casa

fa

clone url: git://git.m455.casa/fa


esperbuild/espersrc/fennel-0.7.0/src/fennel/utils.fnl

1 ;; This module contains mostly general-purpose table-related functionality that
2 ;; you might expect to see in a standard library in most langugaes, as well as
3 ;; the definitions of several core compiler types. It could be split into two
4 ;; distinct modules along those lines.
5
6 (fn stablepairs [t]
7 "Like pairs, but gives consistent ordering every time. On 5.1, 5.2, and LuaJIT
8 pairs is already stable, but on 5.3+ every run gives different ordering."
9 (let [keys []
10 succ []]
11 (each [k (pairs t)]
12 (table.insert keys k))
13 (table.sort keys (fn [a b] (< (tostring a) (tostring b))))
14 (each [i k (ipairs keys)]
15 (tset succ k (. keys (+ i 1))))
16 (fn stablenext [tbl idx]
17 (if (= idx nil)
18 (values (. keys 1) (. tbl (. keys 1)))
19 (values (. succ idx) (. tbl (. succ idx)))))
20 (values stablenext t nil)))
21
22 (fn map [t f out]
23 "Map function f over sequential table t, removing values where f returns nil.
24 Optionally takes a target table to insert the mapped values into."
25 (let [out (or out [])
26 f (if (= (type f) "function")
27 f
28 (let [s f] (fn [x] (. x s))))]
29 (each [_ x (ipairs t)]
30 (match (f x)
31 v (table.insert out v)))
32 out))
33
34 (fn kvmap [t f out]
35 "Map function f over key/value table t, similar to above, but it can return a
36 sequential table if f returns a single value or a k/v table if f returns two.
37 Optionally takes a target table to insert the mapped values into."
38 (let [out (or out [])
39 f (if (= (type f) "function")
40 f
41 (let [s f] (fn [x] (. x s))))]
42 (each [k x (stablepairs t)]
43 (let [(korv v) (f k x)]
44 (when (and korv (not v))
45 (table.insert out korv))
46 (when (and korv v)
47 (tset out korv v))))
48 out))
49
50 (fn copy [from to]
51 "Returns a shallow copy of its table argument. Returns an empty table on nil."
52 (let [to (or to [])]
53 (each [k v (pairs (or from []))]
54 (tset to k v))
55 to))
56
57 (fn member? [x tbl n]
58 (match (. tbl (or n 1))
59 x true
60 nil false
61 _ (member? x tbl (+ (or n 1) 1))))
62
63 (fn allpairs [tbl]
64 "Like pairs, but if the table has an __index metamethod, it will recurisvely
65 traverse upwards, skipping duplicates, to iterate all inherited properties"
66 (assert (= (type tbl) "table") "allpairs expects a table")
67 (var t tbl)
68 (let [seen []]
69 (fn allpairs-next [_ state]
70 (let [(next-state value) (next t state)]
71 (if (. seen next-state)
72 (allpairs-next nil next-state)
73 next-state
74 (do (tset seen next-state true)
75 (values next-state value))
76 (let [meta (getmetatable t)]
77 (when (and meta meta.__index)
78 (set t meta.__index)
79 (allpairs-next t))))))
80 allpairs-next))
81
82 (fn deref [self]
83 "Get the name of a symbol."
84 (. self 1))
85
86 (var nil-sym nil) ; haven't defined sym yet; create this later
87
88 (fn list->string [self tostring2]
89 (var (safe max) (values [] 0))
90 (each [k (pairs self)]
91 (when (and (= (type k) "number") (> k max))
92 (set max k)))
93 (for [i 1 max]
94 (tset safe i (or (and (= (. self i) nil) nil-sym) (. self i))))
95 (.. "(" (table.concat (map safe (or tostring2 tostring)) " " 1 max) ")"))
96
97 (local symbol-mt {1 "SYMBOL" :__fennelview deref :__tostring deref})
98 (local expr-mt {1 "EXPR" :__tostring deref})
99 (local list-mt {1 "LIST" :__fennelview list->string :__tostring list->string})
100 (local sequence-marker ["SEQUENCE"])
101 (local vararg (setmetatable ["..."]
102 {1 "VARARG" :__fennelview deref :__tostring deref}))
103
104 (local getenv (or (and os os.getenv) (fn [] nil)))
105
106 (fn debug-on? [flag]
107 (let [level (or (getenv "FENNEL_DEBUG") "")]
108 (or (= level "all") (level:find flag))))
109
110 (fn list [...]
111 "Create a new list. Lists are a compile-time construct in Fennel; they are
112 represented as tables with a special marker metatable. They only come from
113 the parser, and they represent code which comes from reading a paren form;
114 they are specifically not cons cells."
115 (setmetatable [...] list-mt))
116
117 (fn sym [str scope source]
118 "Create a new symbol. Symbols are a compile-time construct in Fennel and are
119 not exposed outside the compiler. Symbols have source data describing what
120 file, line, etc that they came from."
121 (let [s {:scope scope 1 str}]
122 (each [k v (pairs (or source []))]
123 (when (= (type k) "string")
124 (tset s k v)))
125 (setmetatable s symbol-mt)))
126
127 (set nil-sym (sym "nil"))
128
129 (fn sequence [...]
130 "Create a new sequence. Sequences are tables that come from the parser when
131 it encounters a form with square brackets. They are treated as regular tables
132 except when certain macros need to look for binding forms, etc specifically."
133 ;; can't use SEQUENCE-MT directly as the sequence metatable like we do with
134 ;; the other types without giving up the ability to set source metadata
135 ;; on a sequence, (which we need for error reporting) so embed a marker
136 ;; value in the metatable instead.
137 (setmetatable [...] {:sequence sequence-marker}))
138
139 (fn expr [strcode etype]
140 "Create a new expression. etype should be one of:
141 :literal literals like numbers, strings, nil, true, false
142 :expression Complex strings of Lua code, may have side effects, etc
143 but is an expression
144 :statement Same as expression, but is also a valid statement (function calls)
145 :vargs varargs symbol
146 :sym symbol reference"
147 (setmetatable {:type etype 1 strcode} expr-mt))
148
149 (fn varg [] vararg)
150
151 (fn expr? [x]
152 "Checks if an object is an expression. Returns the object if it is."
153 (and (= (type x) "table") (= (getmetatable x) expr-mt) x))
154
155 (fn varg? [x]
156 "Checks if an object is the vararg symbol. Returns the object if is."
157 (and (= x vararg) x))
158
159 (fn list? [x]
160 "Checks if an object is a list. Returns the object if is."
161 (and (= (type x) "table") (= (getmetatable x) list-mt) x))
162
163 (fn sym? [x]
164 "Checks if an object is a symbol. Returns the object if it is."
165 (and (= (type x) "table") (= (getmetatable x) symbol-mt) x))
166
167 (fn table? [x]
168 "Checks if an object any kind of table, EXCEPT list or symbol or vararg."
169 (and (= (type x) "table")
170 (not= x vararg)
171 (not= (getmetatable x) list-mt)
172 (not= (getmetatable x) symbol-mt)
173 x))
174
175 (fn sequence? [x]
176 "Checks if an object is a sequence (created with a [] literal)"
177 (let [mt (and (= (type x) "table") (getmetatable x))]
178 (and mt (= mt.sequence sequence-marker) x)))
179
180 (fn multi-sym? [str]
181 "A multi symbol is a symbol that is actually composed of two or more symbols
182 using dot syntax. The main differences from normal symbols is that they can't
183 be declared local, and they may have side effects on invocation (metatables)."
184 (if (sym? str) (multi-sym? (tostring str))
185 (not= (type str) "string") false
186 (let [parts []]
187 (each [part (str:gmatch "[^%.%:]+[%.%:]?")]
188 (let [last-char (part:sub (- 1))]
189 (when (= last-char ":")
190 (set parts.multi-sym-method-call true))
191 (if (or (= last-char ":") (= last-char "."))
192 (tset parts (+ (# parts) 1) (part:sub 1 (- 2)))
193 (tset parts (+ (# parts) 1) part))))
194 (and (> (# parts) 0) (or (: str "match" "%.") (: str "match" ":"))
195 (not (str:match "%.%."))
196 (not= (str:byte) (string.byte "."))
197 (not= (str:byte (- 1)) (string.byte "."))
198 parts))))
199
200 (fn quoted? [symbol] symbol.quoted)
201
202 (fn walk-tree [root f custom-iterator]
203 "Walks a tree (like the AST), invoking f(node, idx, parent) on each node.
204 When f returns a truthy value, recursively walks the children."
205 (fn walk [iterfn parent idx node]
206 (when (f idx node parent)
207 (each [k v (iterfn node)]
208 (walk iterfn node k v))))
209 (walk (or custom-iterator pairs) nil nil root)
210 root)
211
212 (local lua-keywords ["and" "break" "do" "else" "elseif" "end" "false" "for"
213 "function" "if" "in" "local" "nil" "not" "or" "repeat"
214 "return" "then" "true" "until" "while" "goto"])
215
216 (each [i v (ipairs lua-keywords)]
217 (tset lua-keywords v i))
218
219 (fn valid-lua-identifier? [str]
220 (and (str:match "^[%a_][%w_]*$") (not (. lua-keywords str))))
221
222 (local propagated-options [:allowedGlobals :indent :correlate :useMetadata
223 :env :compiler-env])
224
225 (fn propagate-options [options subopts]
226 "Certain options should always get propagated onwards when a function that
227 has options calls down into compile."
228 (each [_ name (ipairs propagated-options)]
229 (tset subopts name (. options name)))
230 subopts)
231
232 (local root {:chunk nil :scope nil :options nil :reset (fn [])})
233
234 (fn root.set-reset [{: chunk : scope : options : reset}]
235 (fn root.reset []
236 (set (root.chunk root.scope root.options root.reset)
237 (values chunk scope options reset))))
238
239 (fn hook [event ...]
240 (when (and root.options root.options.plugins)
241 (each [_ plugin (ipairs root.options.plugins)]
242 (match (. plugin event)
243 f (f ...)))))
244
245 {;; general table functions
246 : allpairs : stablepairs : copy : kvmap : map : walk-tree : member?
247
248 ;; AST functions
249 : list : sequence : sym : varg : deref : expr
250 : expr? : list? : multi-sym? : sequence? : sym? : table? : varg? : quoted?
251
252 ;; other
253 : valid-lua-identifier? : lua-keywords : hook
254 : propagate-options : root : debug-on?
255 :path (table.concat ["./?.fnl" "./?/init.fnl" (getenv "FENNEL_PATH")] ";")}