├── .gitignore
├── LICENSE
├── README.md
├── eg
├── dummy.lua
├── example-mac.hy
├── examples.hua
└── examples.lua
├── hua
├── __init__.py
├── cmdline.hy
├── compiler.hy
├── core
│ ├── __init__.py
│ ├── assignment.hy
│ ├── comp.hy
│ ├── hua_stdlib.hua
│ ├── hua_stdlib.lua
│ ├── initialize.hy
│ ├── macros.hy
│ ├── module.hy
│ ├── moses.lua
│ ├── oo.hy
│ ├── op.hy
│ └── utils.hy
├── lua.hy
├── mlast.hy
└── tlcode.lua
├── setup.py
└── tests
└── native_tests
├── core.hua
├── defclass.hua
├── language.hua
├── luaunit.lua
├── reader_macros.hua
├── reader_macros.hy
├── runtest.sh
└── testall.hua
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.egg-info
3 | venv/
4 | tests/native_tests/*.lua
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Permission is hereby granted, free of charge, to any person obtaining a
2 | copy of this software and associated documentation files (the "Software"),
3 | to deal in the Software without restriction, including without limitation
4 | the rights to use, copy, modify, merge, publish, distribute, sublicense,
5 | and/or sell copies of the Software, and to permit persons to whom the
6 | Software is furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in
9 | all copies or substantial portions of the Software.
10 |
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
14 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
16 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17 | DEALINGS IN THE SOFTWARE.
18 |
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Hua
2 |
3 | Hua (㕦) is a [hy](https://github.com/hylang/hy)-like lisp language to lua compiler. It utilizes hy's parser and macro expansion mechanism and compiles the parsed hy syntax tree to [metalua AST](https://github.com/fab13n/metalua/blob/master/doc/ast.txt). The result is then passed to a lua runtime (using the python/lua bridge [lupa](https://github.com/scoder/lupa)) and use [Typed Lua](https://github.com/andremm/typedlua)'s code generator to generate lua codes. Hua is a work in progress and should be considered pre-alpha quality code right now (though I have been using hua to write lua codes scripting [Max](https://cycling74.com/products/max/) for the past month and everything seems to be OK).
4 |
5 | ## Why?
6 |
7 | The main benefit of hua over lua is the ability of meta-programming. Hua includes macros to do hy-style list and dict comprehension, a (still naive) OO system and some other tiny neat features. Though currently [l2l](https://github.com/meric/l2l) and [moonlisp](https://github.com/leafo/moonlisp) may be more mature alternatives.
8 |
9 | One of the benefit of hua over hy is speed, especially if you run the compiled lua code with [luajit](http://luajit.org). Hua (and lua) has a proper lexical scoping implementation. For example, nested `let` is broken in hy, while hua has full support of nested `let`. Lua is widely used to be embedded in host application so for many softwares lua is the only option to extend the softwares capability.
10 |
11 | ## Quickstart
12 |
13 | 1. Create and activate a virtualenv
14 | 2. Install [lupa](https://github.com/scoder/lupa). It's a little bit tricky on Mac. If you have any troubles, please refer to [this guide](https://gist.github.com/larme/9079789cdb1f2fb72b34). (Side notes: currently hua uses [Typed Lua](https://github.com/andremm/typedlua)'s ast->code compiler to generate lua codes so a lua runtime in python (i.e. lupa) is required. I plan to write a native compiler that compiles hua to lua so at least the compiling process doesn't require a lua runtime. However I also plan to add repl to hua which will need the lua runtime. Hence lupa will still be a dependancy in future if you want to use the repl.)
15 | 3. You need to install hua from the git repository: `git clone https://github.com/larme/hua.git; cd hua`
16 | 4. `pip install -e .`
17 | 5. Now you can try the compiler by cding into `eg/` and typing `huac example.hua`. The output will be `example.lua`.
18 |
19 |
20 | ## Brief Introduction aka Comparison with Hy
21 |
22 | Read `example.hua` and its output to get a quick idea of how hua codes look like and what they produce.
23 |
24 | Like hy is a lispy python, hua is a lispy lua. It follow the conventions of lua, which means though hua compiler try to act like hy as much as possible, some aspects are still different.
25 |
26 | ### hua macros are written in hy
27 |
28 | Because currently hua is just a compiler and the compiler is written in hy, hua macros also need to written in hy.
29 |
30 | ### `require`, `require-macro` and `hua-import`
31 |
32 | Because lua use `require` to import a module, we use `require-macro` to require macros from a hy file.
33 |
34 | You can just use `require` to require lua modules, or you can use `hua-import`, which works like `import` in hy.
35 |
36 | ### `setv` vs. `def`
37 |
38 | Because lua is not local by default, `setv` and `def` have different meanings in hua (unlike in hy). `def` is used to introduce a local variable and give it an initial value. `setv` will mutate a variable's value or introduce a global variable.
39 |
40 | Frankly speaking I think in this way the code is more clear and readable.
41 |
42 | ``` lisp
43 | (def x 10)
44 | (setv x 20)
45 | ```
46 |
47 | is compiled to
48 |
49 | ```
50 | local x = 10
51 | x = 20
52 | ```
53 |
54 | There's no way to declare a local variable without assigning an initial value to the variable (like `local x` in lua), just use `(def x nil)`. (Or we can introduce a new keyword `local` which works just like lua's `local`)
55 |
56 | ### Multiple Assignment / Returned Value
57 |
58 | The multiple assignment in lua and python are quite different. Assuming a function `foo` return two value `1, 2`, then after `x, y = foo()`, `x` will be 1 and `y` will be 2 in both python and lua. However after the execution of the following codes:
59 |
60 | ```
61 | x = foo()
62 | ```
63 |
64 | In python `x` will have the value of a tuple `(1, 2)`, while in lua `x` will have the value 1.
65 |
66 | Hua follows lua's behavior, sometimes it may cause unexpected problems. Considering the following codes:
67 |
68 | ``` lisp
69 | (print
70 | (if true
71 | (foo)
72 | (bar)))
73 | ```
74 |
75 | You may think this expression will print out `1 2`. However due to the way how hua compiler works, it will actually print out `1`. The reason is that because the above hua code will be compiled to the following lua code:
76 |
77 | ``` lua
78 | local _hua_anon_var_1
79 | if true then
80 | _hua_anon_var_1 = foo()
81 | else
82 | _hua_anon_var_1 = bar()
83 | end
84 | print(_hua_anon_var_1)
85 | ```
86 |
87 | To prevent this kinds of mistakes, you have to handle the multiple returned values explicitly. One way is to use multiple assignment to assign several variables to the returned values. You need to know how many returned values you will use in advance.
88 |
89 | ``` lisp
90 | (setv (, x y)
91 | (if true
92 | (foo)
93 | (bar)))
94 | (print x y)
95 | ```
96 |
97 | which will compile to:
98 |
99 | ``` lua
100 | local _hua_anon_var_2, _hua_anon_var_3
101 | if true then
102 | _hua_anon_var_2, _hua_anon_var_3 = foo()
103 | else
104 | _hua_anon_var_2, _hua_anon_var_3 = bar()
105 | end
106 | x, y = _hua_anon_var_2, _hua_anon_var_3
107 | print(x,y)
108 | ```
109 |
110 | Another way is packing the multiple returned values into a table using `[(foo)]` syntax.
111 |
112 | ``` lisp
113 | (print
114 | (unpack
115 | (if true
116 | [(foo)]
117 | [(bar)])))
118 | ```
119 |
120 | will compile to
121 |
122 | ``` lua
123 | local _hua_anon_var_1
124 | if true then
125 | _hua_anon_var_1 = {foo()}
126 | else
127 | _hua_anon_var_1 = {bar()}
128 | end
129 | print(unpack(_hua_anon_var_1))
130 | ```
131 |
132 |
133 |
134 | ### `for` loop syntax
135 |
136 | Lua has both generic for and numeric for. The generic for works similar to python's for. However because lua's table is not iterable itself, you need to call `pairs` or `ipairs` on the table. The following hua code:
137 |
138 | ``` lisp
139 | (def t1 [3 4 5 6])
140 | (def t2 {"one" 1 "two" 2})
141 | (for [(, i v1) (ipairs t1)
142 | (, k v2) (pairs t2)]
143 | (print i v1 k v2))
144 |
145 | ```
146 |
147 | will compiled to:
148 |
149 | ``` lua
150 | local t1 = {3, 4, 5, 6}
151 | local t2 = {two = 2, one = 1}
152 | for i, v1 in ipairs(t1) do
153 | for k, v2 in pairs(t2) do
154 | print(i,v1,k,v2)
155 | end
156 | end
157 | ```
158 |
159 | Because you will always call functions like `pairs` and `iparis` on table in the `for` expression, I save the syntax `(for [x [i1 i2 i3]] ...)` for the numeric for statement in lua. Something like `(for [i [1 14 3]] (print i))` will compile to:
160 |
161 | ``` lua
162 | for i = 1, 14, 3 do
163 | print(i)
164 | end
165 | ```
166 |
167 | You can mix numeric for and generic for in the same `for` expression. The following codes:
168 |
169 | ``` lisp
170 | (for [i [1 3]
171 | key (pairs {"pos" 3 "cons" 2})]
172 | (print "mixed:" i key))
173 | ```
174 |
175 | will compiles to:
176 |
177 | ``` lua
178 | for i = 1, 3 do
179 | for key in pairs({pos = 3, cons = 2}) do
180 | print("mixed:",i,key)
181 | end
182 | end
183 | ```
184 |
185 |
186 |
187 | ### List and Dict comprehensive
188 |
189 | Hua's list and dict comprehensive are macros that expanded to `for` expressions. So the syntax is different to hy's but similar to hua's `for`
190 |
191 | ``` lisp
192 | (def l (list-comp (* i value)
193 | [i [1 10]
194 | (, _ value) (pairs {"T" 1 "F" 0})]))
195 | ```
196 |
197 | will compile to
198 |
199 | ``` lua
200 | local l
201 | do
202 | local _hua_result_1235 = {}
203 | for i = 1, 10 do
204 | for _, value in pairs({T = 1, F = 0}) do
205 | _hua_result_1235[(1 + #(_hua_result_1235))] = (i * value)
206 | end
207 | end
208 | l = _hua_result_1235
209 | end
210 | ```
211 |
212 |
213 |
214 | ### Tail Call Optimization
215 |
216 | Unlike python, lua has tail call optimization. However if you define some recursive functions like below, it may blow the stack:
217 |
218 | ``` lisp
219 | (defn test-tco [n]
220 | (if (> n 1)
221 | (test-tco (- n 1))
222 | (print "May overflow here")))
223 | ```
224 |
225 | The compiled lua code explained why:
226 |
227 | ``` lua
228 | local test_tco = nil
229 | test_tco = function (n)
230 | local _hua_anon_var_7
231 | if not (n <= 1) then
232 | _hua_anon_var_7 = test_tco((n - 1))
233 | else
234 | _hua_anon_var_7 = print("May overflow here")
235 | end
236 | return _hua_anon_var_7
237 | end
238 | ```
239 |
240 | Use `return` directly will solve this problem (at least for simple recursive form).
241 |
242 | ``` lisp
243 | (defn test-tco2 [n]
244 | (if (> n 1)
245 | (return (test-tco (- n 1)))
246 | (print "No overflow!")))
247 | ```
248 |
249 | will compiles to:
250 |
251 | ``` lua
252 | local test_tco2 = nil
253 | test_tco2 = function (n)
254 | local _hua_anon_var_2
255 | if not (n <= 1) then
256 | return test_tco((n - 1))
257 | else
258 | _hua_anon_var_2 = print("No overflow!")
259 | end
260 | return _hua_anon_var_2
261 | end
262 | ```
263 |
264 | However please don't overuse `return`. If you want to return a table at the end of a file (as a module table), consider using `export` at the end
265 |
266 | ### No Keyword Arguments for function
267 |
268 | Because lua doesn't support named arguments, hua will not support keyword arguments.
269 |
270 | ### "dot call"s
271 |
272 | In hy `(foo.bar "hello")` is the same as `(.bar foo "hello")`. They both invoke a method `bar` of object `foo` with a single parameter `"hello"`. However in hua it's entirely different. `(foo.bar "hello")` compiles to `foo.bar("hello")` while `(.bar foo "hello")` compiles to `foo:bar("hello")`. In both case `bar` is a property of table `foo`, however in the first case `bar` is called with a single argument `"hello"` while in the second case `bar` is called with two arguments: table `foo` and `"hello"`.
273 |
274 | ### `defclass` difference
275 |
276 | `defclass` works like hy's one. Use `--init` instead of `--init--`. When defining a class without parent class, `--init` method is mandatory.
277 |
278 | ``` lisp
279 | ;; A class without parent class.
280 | ;; A --init method is required for class without parent class.
281 | (defclass Animal []
282 | [[--init
283 | (fn [self steps-per-turn]
284 | (setv self.steps-per-turn steps-per-turn))]
285 | [move
286 | (fn [self]
287 | (print (concat "I moved "
288 | (tostring self.steps-per-turn)
289 | " steps!")))]])
290 |
291 | ;; Hua only support single inheritance.
292 | ;; Use `(super method paras...)' to call parent's method
293 | (defclass Cat [Animal]
294 | [[--init
295 | (fn [self steps-per-turn sound]
296 | (super --init steps-per-turn)
297 | (setv self.sound sound))]
298 | [move
299 | (fn [self]
300 | (print self.sound)
301 | (super move))]])
302 | ```
303 |
304 |
305 |
306 | ### Misc
307 |
308 | Nearly any lua vs python differences will hold in hua vs hy. Some examples: hua has correct lexical scoping so nested `let` works; only `nil` and `false` are false in hua while empty string and list are also false in hy etc; hua table are not iterable like python's dict and list.
309 |
310 | ## To be Improved
311 |
312 | By priority:
313 |
314 | - No sane error messages, no error codes line numbers!
315 |
316 | - No enough test cases yet!
317 |
318 | - A repl may be needed for some test cases.
319 |
320 | - Currently every hua file need to begin with some initializing codes mainly to import standard macros into the hua file (see examples.hua). This should be done automatically.
321 |
322 | - Currently every lua file output by hua compiler will add `hua/hua/core/` into lua's package path and auto require some functions in `hua/hua/core/hua_stdlib.lua`. The motif is that every lua file compiled by hua is usable immediately without any further configuration. However this is not a good idea because that means every lua file produced by hua compiler is not portable between machines. Later we may pack the `hua_stdlib.lua` as a luarock or ask users to copy the file to their lua package path.
323 |
324 | - Talking about `hua_stdlib.lua`, we may add more functional list/dict manipulating functions using the excellent [Moses](https://github.com/Yonaba/Moses) library.
325 |
326 | - ~~`huac` can only compile one file at one time.~~
327 |
328 | - Repl, again
329 |
330 | - Native compiler in hy.
331 |
332 | - Docstring for function definition? It's quite doable because `defn/defun` is just a macro.
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 | ## Acknowledgements
342 |
343 | Thanks the authors of [hy](https://github.com/hylang/hy), [lupa](https://github.com/scoder/lupa) and [Typed Lua](https://github.com/andremm/typedlua). They wrote the most crucial parts and I'm just glueing these parts together.
--------------------------------------------------------------------------------
/eg/dummy.lua:
--------------------------------------------------------------------------------
1 | local m = {}
2 |
3 | m.dummy1 = 1
4 | m.dummy2 = 2
5 |
6 | return m
7 |
--------------------------------------------------------------------------------
/eg/example-mac.hy:
--------------------------------------------------------------------------------
1 | (import [hy.models.string [HyString]])
2 |
3 | (defmacro defsection [sect-name &rest body]
4 | (def sep (HyString "----------------------------"))
5 | (def end (HyString "------- section ends -------\n"))
6 | `(do
7 | (print ~sect-name)
8 | (print ~sep)
9 | (do ~@body)
10 | (print ~end)))
11 |
12 |
--------------------------------------------------------------------------------
/eg/examples.hua:
--------------------------------------------------------------------------------
1 | ;;; Currently you need put these two lines below to the beginning of
2 | ;;; each hua file. As it will requires some core hua macros like
3 | ;;; `let', `for' etc.
4 |
5 | (require-macro "hua.core.initialize")
6 | (--hua-initialize--)
7 |
8 | ;;; Require the macro `defsection' from in hy file "example-mac.hy"
9 | (require-macro "example-mac")
10 |
11 | ;;; Simple assignments and arithmetic
12 | (defsection "Simple Assignments and Arithmetic"
13 | (def x 10)
14 | (setv x 20)
15 | ;; a global variable y is introduced
16 | (setv y 40)
17 | (setv z 20)
18 |
19 | ;; arithmetic operators need not be binary
20 | (print "Some arithmetic operations"
21 | (+ x y z)
22 | (< x y z) ; you can chain comparison operators
23 | (- x)))
24 |
25 | ;;; Table
26 | (defsection "Table as Dict and List"
27 | ;; Lua has table as its only data structure. However in hua we still
28 | ;; have dictionary and list syntax like hy.
29 | (def a-table {"name" "John"})
30 | (setv a-table.greeting "Hello, ")
31 | (print (concat a-table.greeting
32 | a-table.name
33 | "!")) ; `concat' works like .. in lua
34 | (def a-list [1 2 3 4])
35 | (print "a-list length:" (len a-list))
36 |
37 | ;; access table use `get', use integer index for the array part and
38 | ;; string index for the dict part
39 | (setv a-table.l a-list)
40 | (print "a-table.l[3]:" (get a-table "l" 3))
41 |
42 | ;; change the value of a key in table using assoc
43 | (assoc a-table "l" 3 13)
44 | (print "a-table.l[3] after assoc change the value:"
45 | (get a-table "l" 3)))
46 |
47 | (defsection "Statements"
48 |
49 | ;; multiple assignment works in left hand side, use `unpack' in
50 | ;; right hand side
51 | (def (, m n) (unpack [1 2]))
52 | (print "m, n:" m n)
53 |
54 | ;; `if' and `cond' work like hy
55 | (if (= x y)
56 | (print "x is equal to y")
57 | (print "x is not equal to y"))
58 |
59 | (cond [(< z 20) (print "z is smaller than 20")]
60 | [(= z 20) (print "z is equal to 20")]
61 | [(> z 20) (print "z is larger than 20")]
62 | [true (print "z is 42")])
63 |
64 | ;; generic `for' works like hy. However table in lua is not
65 | ;; iterable, you need `pairs' or `ipairs'
66 | (for [(, i v) (ipairs [1 3 5])]
67 | (print "index and value in list:" i v))
68 |
69 | ;; numeric for
70 | (for [i [1 11 2]]
71 | (print "numeric for:" i))
72 |
73 | ;; You can mix both syntax in one `for' expression
74 | (for [i [1 3]
75 | key (pairs {"pos" 3 "cons" 2})]
76 | (print "mixed:" i key))
77 |
78 | ;; You can do hy style list and dict comprehensive! They are just
79 | ;; macros expanded to
80 | (def l (list-comp (* i value)
81 | [i [1 10]
82 | (, _ value) (pairs {"T" 1 "F" 0})]))
83 | (for [(, _ v) (ipairs l)]
84 | (print "list comprehensive result:" v)))
85 |
86 | (defsection "Functions"
87 | ;; Function definition
88 | (defn double [n]
89 | (* n 2))
90 | (print "double of 21:" (double 21))
91 |
92 | ;; You can use &rest keyword now. I may add support of &optional
93 | ;; later, but lua makes &optional quite redundant.
94 | ;; Notice that &rest arguments cannot contain `nil's
95 |
96 | (defn print-alot [a &rest rest]
97 | (print "first parameter:" a)
98 | (for [(, i para) (ipairs rest)]
99 | (print "rest parameter:" para)))
100 |
101 | (print-alot "That's" "really" "a" "lot")
102 | (print-alot "That's" nil "really" "a" "lot")
103 |
104 | ;; Normal function definition in hua cannot do proper tail calls,
105 | ;; some recursive functions like below will blow the stack.
106 |
107 | (defn test-tco [n]
108 | (if (> n 1)
109 | (test-tco (- n 1))
110 | (print "May overflow here, read the lua code to find out why")))
111 | ;; Uncomment the following line to make a stack overflow error.
112 | ;; (test-tco 10000000)
113 |
114 | ;; To overcome this problem use `(return (f))' at the end of a
115 | ;; branch. FIXME: make this part more clear.
116 |
117 | (defn test-tco2 [n]
118 | (if (> n 1)
119 | (return (test-tco2 (- n 1)))
120 | (print "No overflow!")))
121 | (test-tco2 10000000)
122 |
123 | (defn test-tco3 [n]
124 | (cond [(> n 10)
125 | (return (test-tco3 (- n 2)))]
126 | [(>= 10 n 2)
127 | (return (test-tco3 (- n 1)))]
128 | [true
129 | (print "No overflow too!")]))
130 | (test-tco3 10000000))
131 |
132 | (defsection "Object Oriented"
133 |
134 | ;; hua's OO system is not very complete yet, for example it
135 | ;; currently lacks something like `isinstance' in python
136 |
137 | (defclass Animal []
138 | [[--init
139 | (fn [self steps-per-turn]
140 | (setv self.steps-per-turn steps-per-turn))]
141 | [move
142 | (fn [self]
143 | (print (concat "I moved "
144 | (tostring self.steps-per-turn)
145 | " steps!")))]])
146 |
147 | ;; Hua only support single inheritance.
148 | ;; Use `(super method paras...)' to call parent's method
149 | (defclass Cat [Animal]
150 | [[--init
151 | (fn [self steps-per-turn sound]
152 | (super --init steps-per-turn)
153 | (setv self.sound sound))]
154 | [move
155 | (fn [self]
156 | (print self.sound)
157 | (super move))]])
158 |
159 | (def a-cat (Cat 3 "meow meow meow!"))
160 | (.move a-cat))
161 |
162 |
163 | (defsection "Modules"
164 | ;; you can use `require' as in lua, but you can also `hua-import' to
165 | ;; import like hy.
166 | (hua-import [dummy [dummy1 dummy2]])
167 | (print "Variables in module \"dummy\"" dummy1 dummy2)
168 |
169 | ;; at the end of a file, use `export' to export some local
170 | ;; variable. Of course you can use `return' to manually do that.
171 | (export test-tco3))
172 |
--------------------------------------------------------------------------------
/eg/examples.lua:
--------------------------------------------------------------------------------
1 | package.path = string.format("%s;%s/?.lua",package.path,"/Users/larme/codes/hua/hua/core")
2 | local unpack = (unpack or table.unpack)
3 | local apply = nil
4 | local dec = nil
5 | local first = nil
6 | local inc = nil
7 | local _hua_anon_var_1
8 | do
9 | local __hua_import_m__ = require("hua_stdlib")
10 | apply = __hua_import_m__.apply
11 | dec = __hua_import_m__.dec
12 | first = __hua_import_m__.first
13 | inc = __hua_import_m__.inc
14 | _hua_anon_var_1 = nil
15 | end
16 | print("Simple Assignments and Arithmetic")
17 | print("----------------------------")
18 | local x = 10
19 | x = 20
20 | y = 40
21 | z = 20
22 | print("Some arithmetic operations",(x + y + z),(x < y and y < z), - (x))
23 | print("------- section ends -------\n")
24 | print("Table as Dict and List")
25 | print("----------------------------")
26 | local a_table = {name = "John"}
27 | a_table.greeting = "Hello, "
28 | print((a_table.greeting .. a_table.name .. "!"))
29 | local a_list = {1, 2, 3, 4}
30 | print("a-list length:",#(a_list))
31 | a_table.l = a_list
32 | print("a-table.l[3]:",a_table.l[3])
33 | a_table.l[3] = 13
34 | print("a-table.l[3] after assoc change the value:",a_table.l[3])
35 | print("------- section ends -------\n")
36 | print("Statements")
37 | print("----------------------------")
38 | local m, n = unpack({1, 2})
39 | print("m, n:",m,n)
40 | local _hua_anon_var_2
41 | if x == y then
42 | _hua_anon_var_2 = print("x is equal to y")
43 | else
44 | _hua_anon_var_2 = print("x is not equal to y")
45 | end
46 | local _hua_anon_var_6
47 | if z < 20 then
48 | _hua_anon_var_6 = print("z is smaller than 20")
49 | else
50 | local _hua_anon_var_5
51 | if z == 20 then
52 | _hua_anon_var_5 = print("z is equal to 20")
53 | else
54 | local _hua_anon_var_4
55 | if not (z <= 20) then
56 | _hua_anon_var_4 = print("z is larger than 20")
57 | else
58 | local _hua_anon_var_3
59 | if true then
60 | _hua_anon_var_3 = print("z is 42")
61 | else
62 | _hua_anon_var_3 = nil
63 | end
64 | _hua_anon_var_4 = _hua_anon_var_3
65 | end
66 | _hua_anon_var_5 = _hua_anon_var_4
67 | end
68 | _hua_anon_var_6 = _hua_anon_var_5
69 | end
70 | for i, v in ipairs({1, 3, 5}) do
71 | print("index and value in list:",i,v)
72 | end
73 | for i = 1, 11, 2 do
74 | print("numeric for:",i)
75 | end
76 | for i = 1, 3 do
77 | for key in pairs({pos = 3, cons = 2}) do
78 | print("mixed:",i,key)
79 | end
80 | end
81 | local l
82 | do
83 | local _hua_result_1235 = {}
84 | for i = 1, 10 do
85 | for _, value in pairs({T = 1, F = 0}) do
86 | _hua_result_1235[(1 + #(_hua_result_1235))] = (i * value)
87 | end
88 | end
89 | l = _hua_result_1235
90 | end
91 | for _, v in ipairs(l) do
92 | print("list comprehensive result:",v)
93 | end
94 | print("------- section ends -------\n")
95 | print("Functions")
96 | print("----------------------------")
97 | local double = nil
98 | double = function (n)
99 | return (n * 2)
100 | end
101 | print("double of 21:",double(21))
102 | local print_alot = nil
103 | print_alot = function (a, ...)
104 | local rest = {...}
105 | print("first parameter:",a)
106 | for i, para in ipairs(rest) do
107 | print("rest parameter:",para)
108 | end
109 | end
110 | print_alot("That's","really","a","lot")
111 | print_alot("That's",nil,"really","a","lot")
112 | local test_tco = nil
113 | test_tco = function (n)
114 | local _hua_anon_var_8
115 | if not (n <= 1) then
116 | _hua_anon_var_8 = test_tco((n - 1))
117 | else
118 | _hua_anon_var_8 = print("May overflow here, read the lua code to find out why")
119 | end
120 | return _hua_anon_var_8
121 | end
122 | local test_tco2 = nil
123 | test_tco2 = function (n)
124 | local _hua_anon_var_9
125 | if not (n <= 1) then
126 | return test_tco2((n - 1))
127 | else
128 | _hua_anon_var_9 = print("No overflow!")
129 | end
130 | return _hua_anon_var_9
131 | end
132 | test_tco2(10000000)
133 | local test_tco3 = nil
134 | test_tco3 = function (n)
135 | local _hua_anon_var_12
136 | if not (n <= 10) then
137 | return test_tco3((n - 2))
138 | else
139 | local _hua_anon_var_11
140 | if (not (10 < n) and not (n < 2)) then
141 | return test_tco3((n - 1))
142 | else
143 | local _hua_anon_var_10
144 | if true then
145 | _hua_anon_var_10 = print("No overflow too!")
146 | else
147 | _hua_anon_var_10 = nil
148 | end
149 | _hua_anon_var_11 = _hua_anon_var_10
150 | end
151 | _hua_anon_var_12 = _hua_anon_var_11
152 | end
153 | return _hua_anon_var_12
154 | end
155 | test_tco3(10000000)
156 | print("------- section ends -------\n")
157 | print("Object Oriented")
158 | print("----------------------------")
159 | local Animal = nil
160 | local _hua_anon_var_15
161 | do
162 | local __hua_parent__ = nil
163 | local __hua_base__ = {__init = function (self, steps_per_turn)
164 | self.steps_per_turn = steps_per_turn
165 | end, move = function (self)
166 | return print(("I moved " .. tostring(self.steps_per_turn) .. " steps!"))
167 | end}
168 | local __hua_class_string__ = "Animal"
169 | __hua_base__.__index = __hua_base__
170 | local _hua_anon_var_13
171 | if __hua_parent__ then
172 | _hua_anon_var_13 = setmetatable(__hua_base__,__hua_parent__.__base)
173 | else
174 | _hua_anon_var_13 = nil
175 | end
176 | local __hua_cls_call__ = nil
177 | __hua_cls_call__ = function (cls, ...)
178 | local __hua_self__ = setmetatable({},__hua_base__)
179 | __hua_self__:__init(...)
180 | return __hua_self__
181 | end
182 | local __hua_class__ = setmetatable({__base = __hua_base__, __name = __hua_class_string__, __parent = __hua_parent__},{__call = __hua_cls_call__, __index = __hua_base__})
183 | __hua_base__.__class = __hua_class__
184 | local _hua_anon_var_14
185 | if (__hua_parent__ and __hua_parent__.__inherited) then
186 | _hua_anon_var_14 = __hua_parent__.__inherited(__hua_parent__,__hua_class__)
187 | else
188 | _hua_anon_var_14 = nil
189 | end
190 | Animal = __hua_class__
191 | _hua_anon_var_15 = nil
192 | end
193 | local Cat = nil
194 | local _hua_anon_var_19
195 | do
196 | local __hua_parent__ = Animal
197 | local __hua_base__ = {move = function (self)
198 | print(self.sound)
199 | return __hua_parent__.move(self)
200 | end, __init = function (self, steps_per_turn, sound)
201 | __hua_parent__.__init(self,steps_per_turn)
202 | self.sound = sound
203 | end}
204 | local __hua_class_string__ = "Cat"
205 | __hua_base__.__index = __hua_base__
206 | local _hua_anon_var_16
207 | if __hua_parent__ then
208 | _hua_anon_var_16 = setmetatable(__hua_base__,__hua_parent__.__base)
209 | else
210 | _hua_anon_var_16 = nil
211 | end
212 | local __hua_cls_call__ = nil
213 | __hua_cls_call__ = function (cls, ...)
214 | local __hua_self__ = setmetatable({},__hua_base__)
215 | __hua_self__:__init(...)
216 | return __hua_self__
217 | end
218 | local __hua_class__ = setmetatable({__base = __hua_base__, __name = __hua_class_string__, __parent = __hua_parent__},{__call = __hua_cls_call__, __index = function (cls, name)
219 | local val = rawget(__hua_base__,name)
220 | local _hua_anon_var_17
221 | if val == nil then
222 | _hua_anon_var_17 = __hua_parent__[name]
223 | else
224 | _hua_anon_var_17 = val
225 | end
226 | return _hua_anon_var_17
227 | end})
228 | __hua_base__.__class = __hua_class__
229 | local _hua_anon_var_18
230 | if (__hua_parent__ and __hua_parent__.__inherited) then
231 | _hua_anon_var_18 = __hua_parent__.__inherited(__hua_parent__,__hua_class__)
232 | else
233 | _hua_anon_var_18 = nil
234 | end
235 | Cat = __hua_class__
236 | _hua_anon_var_19 = nil
237 | end
238 | local a_cat = Cat(3,"meow meow meow!")
239 | a_cat:move()
240 | print("------- section ends -------\n")
241 | print("Modules")
242 | print("----------------------------")
243 | local dummy1 = nil
244 | local dummy2 = nil
245 | local _hua_anon_var_20
246 | do
247 | local __hua_import_m__ = require("dummy")
248 | dummy1 = __hua_import_m__.dummy1
249 | dummy2 = __hua_import_m__.dummy2
250 | _hua_anon_var_20 = nil
251 | end
252 | print("Variables in module \"dummy\"",dummy1,dummy2)
253 | local _hua_anon_var_21
254 | do
255 | local _hua_module_1236 = {}
256 | _hua_module_1236.test_tco3 = test_tco3
257 | return _hua_module_1236
258 | end
259 | print("------- section ends -------\n")
260 |
--------------------------------------------------------------------------------
/hua/__init__.py:
--------------------------------------------------------------------------------
1 | import hy
2 |
--------------------------------------------------------------------------------
/hua/cmdline.hy:
--------------------------------------------------------------------------------
1 | (import argparse)
2 | (import sys)
3 |
4 | (import [hua.compiler [compile-file]])
5 |
6 | (defn huac-main []
7 |
8 | (def options {"prog" "huac" "usage" "%(prog)s [options] FILE"})
9 | (def parser (apply argparse.ArgumentParser [] options))
10 | (apply .add-argument
11 | [parser "args"]
12 | {"nargs" argparse.REMAINDER "help" argparse.SUPPRESS})
13 |
14 |
15 | (setv options (.parse-args parser (slice sys.argv 1)))
16 |
17 | (for [filename options.args]
18 | (compile-file filename)))
19 |
--------------------------------------------------------------------------------
/hua/compiler.hy:
--------------------------------------------------------------------------------
1 | (import os.path)
2 |
3 | (import [hy.models.expression [HyExpression]]
4 | [hy.models.keyword [HyKeyword]]
5 | [hy.models.integer [HyInteger]]
6 | [hy.models.float [HyFloat]]
7 | [hy.models.string [HyString]]
8 | [hy.models.symbol [HySymbol]]
9 | [hy.models.list [HyList]]
10 | [hy.models.dict [HyDict]]
11 | [hy.compiler [checkargs]]
12 | [hy.macros]
13 | [hy.importer [import-file-to-hst]])
14 |
15 | (import [hua.mlast :as ast]
16 | [hua.lua [tlast->src]])
17 |
18 | (def -compile-table {})
19 |
20 | (defn ast-str (s)
21 | (% "%s" s))
22 |
23 | (defn builds [-type]
24 | "assoc decorated function to compile-table"
25 | (lambda [f]
26 | (assoc -compile-table -type f)
27 | f))
28 |
29 | (defclass Result [object]
30 | [[--init--
31 | (fn [self &rest args &kwargs kwargs]
32 | (setv self.stmts [])
33 | (setv self.temp-vars [])
34 | (setv self.-expr nil)
35 | (setv self.--used-expr false)
36 |
37 | (for [kwarg kwargs]
38 | (unless (in kwarg ["stmts"
39 | "expr"
40 | "temp_vars"])
41 | (print "something wrong"))
42 | (setattr self kwarg (. kwargs [kwarg])))
43 |
44 | nil)]
45 |
46 | [expr
47 | (with-decorator property
48 | (defn expr [self]
49 | (setv self.--used-expr true)
50 | self.-expr))]
51 | [expr
52 | (with-decorator expr.setter
53 | (defn expr [self value]
54 | (setv self.--used-expr false)
55 | (setv self.-expr value)))]
56 |
57 | [expr?
58 | (fn [self]
59 | "Check whether I am a pure expression"
60 | (and self.-expr
61 | (empty? [])))]
62 |
63 | [force-expr
64 | (with-decorator property
65 | (defn force-expr [self]
66 | "Force the expression context of the Result"
67 | (if self.expr
68 | self.expr
69 | ;; FIXME
70 | (ast.Nil))))]
71 |
72 | [expr-as-stmt
73 | (fn [self]
74 | "Convert the Result's expression context to a statement
75 |
76 | Unlike python, only function/method call can be pure expression statement"
77 |
78 | (if (and self.expr
79 | (instance? ast.Apply self.expr))
80 | (+ (Result) (apply Result [] {"stmts" [self.expr]}))
81 | (Result)))]
82 |
83 | [rename
84 | (fn [self new-names-]
85 | "Rename the Result's temporary variables to a `new-name`"
86 | (def new-names (if (coll? new-names-)
87 | (list-comp (ast-str new-name-)
88 | [new-name- new-names-])
89 | [new-names-]))
90 |
91 | (for [var self.temp-vars]
92 | (cond [(instance? ast.Id var)
93 | (setv var.nodes (get new-names 0))]
94 | [(instance? ast.Multi var)
95 | (do
96 | (def new-ids (list-comp (ast.Id new-name)
97 | [new-name new-names]))
98 | (setv var.exprs new-ids))]
99 | [true
100 | (raise "FIXME")]))
101 | (setv self.temp-vars []))]
102 |
103 | [--add--
104 | (fn [self other]
105 | (cond
106 |
107 | ;; ast.expr case come first because ast.Apply is both statement and expression. By default we will treat them as expression.
108 | [(ast.expr? other)
109 | (+ self (apply Result [] {"expr" other}))]
110 | [(ast.stat? other)
111 | (+ self (apply Result [] {"stmts" [other]}))]
112 |
113 | ;; FIXME
114 | [true
115 | (let [[result (Result)]]
116 | (setv result.stmts (+ self.stmts
117 | other.stmts))
118 | (setv result.expr other.expr)
119 | (setv result.temp-vars other.temp-vars)
120 | result)]))]])
121 |
122 | (defn -branch [results-]
123 | "make a branch out of a list of Result objects"
124 | (let [[results (list results-)]
125 | [ret (Result)]]
126 | (for [result (slice results 0 -1)]
127 | (+= ret result)
128 | (+= ret (.expr-as-stmt result)))
129 | (for [result (slice results -1)]
130 | (+= ret result))
131 | ret))
132 |
133 | (defn -assign-expr-for-result [result var expr]
134 | "If the result's last statement is not ast.Return, append an ast.Set statement of assigning var to expr to the result."
135 | (when (or (empty? result.stmts)
136 | (not (instance? ast.Return
137 | (get result.stmts -1))))
138 | (+= result (ast.Set var expr)))
139 | result)
140 |
141 | (defclass HuaASTCompiler [object]
142 | [[--init--
143 | (fn [self module-name]
144 | (setv self.anon-fn-count 0)
145 | (setv self.anon-var-count 0)
146 | (setv self.module-name module-name)
147 | nil)]
148 |
149 | [get-anon-var
150 | (fn [self]
151 | (+= self.anon-var-count 1)
152 | (% "_hua_anon_var_%s" self.anon-var-count))]
153 |
154 | [get-anon-fn
155 | (fn [self]
156 | (+= self.anon-fn-count 1)
157 | (% "_hua_anon_fn_%s" self.anon-fn-count))]
158 |
159 | [compile-atom
160 | (fn [self atom-type atom]
161 | ;; (print atom-type)
162 | ;; (print atom)
163 | ;; (print (in atom-type -compile-table))
164 | ;; (print "compile-atom ======")
165 | (when (in atom-type -compile-table)
166 | ;; (print "compile-f: " (get -compile-table atom-type))
167 | ;; (print "atom: " atom)
168 | ;; (print "\n")
169 | (let [[compile-f (get -compile-table atom-type)]
170 | [ret (compile-f self atom)]]
171 | (if (instance? Result ret)
172 | ret
173 | (+ (Result) ret)))))]
174 |
175 | [compile
176 | (fn [self tree]
177 | ;;; FIXME compiler errors
178 | ;; (print "compile =====")
179 | (let [[-type (type tree)]]
180 | (.compile-atom self -type tree)))]
181 |
182 | [-compile-collect
183 | (fn [self exprs]
184 | "Collect the expression contexts from a list of compiled expression."
185 | (let [[compiled-exprs []]
186 | [ret (Result)]]
187 | (for [expr exprs]
188 | (+= ret (.compile self expr))
189 | (.append compiled-exprs ret.force_expr))
190 | (, compiled-exprs ret)))]
191 |
192 | [-compile-branch
193 | (fn [self exprs]
194 | (-branch (list-comp (.compile self expr) [expr exprs])))]
195 |
196 | ;;; FIXME no keyword and kwargs yet, maybe never
197 | [-parse-lambda-list
198 | (fn [self exprs]
199 | (def ll-keywords (, "&rest" "&optional"))
200 | (def ret (Result))
201 | (def args [])
202 | (def defaults [])
203 | (def varargs nil)
204 | (def lambda-keyword nil)
205 | (for [expr exprs]
206 | (if (in expr ll-keywords)
207 | ;; FIXME &optional error handling
208 | (setv lambda-keyword expr)
209 | (cond
210 | [(nil? lambda-keyword)
211 | (.append args expr)]
212 | [(= lambda-keyword "&rest")
213 | (if (nil? varargs)
214 | (setv varargs (str expr))
215 | (print "FIXME only one &rest error"))]
216 | [(= lambda-keyword "&optional")
217 | (do
218 | (if (instance? HyList expr)
219 | (if (not (= 2 (len expr)))
220 | (print "FIXME optinal rags hould be bare names"
221 | "or 2-item lists")
222 | (setv (, k v) expr))
223 | (do
224 | (setv k expr)
225 | (setv v (.replace (HySymbol "nil") k))))
226 | (.append args k)
227 | (+= ret (.compile self v))
228 | (.append defaults ret.force_expr))])))
229 | (, ret args defaults varargs))]
230 |
231 |
232 | ;;; FIXME _storeize do we really need this?
233 | [-storeize
234 | (fn [self name]
235 | (if-not (.expr? name)
236 | (print "FIXME: type error")
237 | (setv name name.expr))
238 |
239 | ;;; FIXME multiple assign, index etc.
240 | (cond [(instance? (, ast.Id ast.Index ast.Multi) name)
241 | name]
242 | [true
243 | (print "FIXME: type error")]))]
244 |
245 | [compile-raw-list
246 | (with-decorator (builds list)
247 | (fn [self entries]
248 | (let [[ret (.-compile-branch self entries)]]
249 | (+= ret (.expr-as-stmt ret))
250 | ret)))]
251 |
252 | ;;; FIXME quote related. or no quote because we don't have macro?
253 |
254 | ;;; FIXME a lot of functions in between
255 |
256 | [compile-progn
257 | (with-decorator (builds "do") (builds "progn")
258 | (fn [self expression]
259 | (.pop expression 0)
260 | (.-compile-branch self expression)))]
261 |
262 | [compile-do-block
263 | (with-decorator (builds "do_block")
264 | (fn [self expression]
265 | (.pop expression 0)
266 | (def branch (.-compile-branch self expression))
267 | (def var-name (.get-anon-var self))
268 | (def var (ast.Multi (ast.Id var-name)))
269 | (setv branch
270 | (-assign-expr-for-result branch var branch.force-expr))
271 | (+ (Result)
272 | (ast.Local var)
273 | (ast.Do branch.stmts)
274 | (apply Result
275 | []
276 | {"expr" var "temp_vars" [var]}))))]
277 |
278 | [compile-if
279 | (with-decorator
280 | (builds "if")
281 | (apply checkargs [] {"min" 2 "max" 3})
282 | (fn [self expression]
283 | (.pop expression 0)
284 | (let [[condition (.compile self (.pop expression 0))]
285 | [body (.compile self (.pop expression 0))]
286 | [orel (if (empty? expression)
287 | (Result)
288 | (.compile self (.pop expression 0)))]
289 | [ret condition]
290 |
291 | [var-name (.get-anon-var self)]
292 | [var (ast.Multi (ast.Id var-name))]
293 |
294 | [expr-name (ast.Multi (ast.Id (ast-str var-name)))]]
295 |
296 | ;; we won't test if statements in body or orel because lua doesn't have official ternary operator support
297 |
298 | ;; (+= ret (ast.Local [var]))
299 | (setv ret (+ (Result) (ast.Local var) ret))
300 | (setv body
301 | (-assign-expr-for-result body var body.force-expr))
302 | (setv orel
303 | (-assign-expr-for-result orel var orel.force-expr))
304 | (+= ret (ast.If ret.force-expr body.stmts orel.stmts))
305 | (+= ret (apply Result []
306 | {"expr" expr-name "temp_vars" [expr-name
307 | var]}))
308 | ret
309 | )))]
310 |
311 | ;;; FIXME break, assert etc.
312 |
313 | ;;; FIXME import/require
314 |
315 | [compile-index-expression
316 | (with-decorator
317 | (builds "get")
318 | (apply checkargs [] {"min" 2})
319 | (fn [self expr]
320 | (.pop expr 0)
321 |
322 | (def val (.compile self (.pop expr 0)))
323 | (def (, indexes ret) (.-compile-collect self expr))
324 |
325 | (when (not (empty? val.stmts))
326 | (+= ret val))
327 |
328 | (for [index indexes]
329 | (setv val (+ (Result)
330 | (ast.Index
331 | (let [[val-expr val.force-expr]]
332 | ;; if val.force-expr is a literal table, we
333 | ;; need a pair of parentheses around the
334 | ;; literal table to make the index work
335 | (if (instance? ast.Table val-expr)
336 | (ast.Paren val-expr)
337 | val-expr))
338 | index))))
339 | (+ ret val)))]
340 |
341 | [compile-multi
342 | (with-decorator (builds ",")
343 | (fn [self expr]
344 | (.pop expr 0)
345 | (def (, elts ret) (.-compile-collect self expr))
346 | (def multi (ast.Multi elts))
347 | (+= ret multi)
348 | ret))]
349 |
350 | [compile-require-macro
351 | (with-decorator (builds "require_macro")
352 | (fn [self expression]
353 | (.pop expression 0)
354 | (for [entry expression]
355 | (--import-- entry)
356 | (hy.macros.require entry self.module-name))
357 | (Result)))]
358 |
359 | [compile-compare-op-expression
360 | (with-decorator
361 | (builds "=*")
362 | (builds "<*")
363 | (builds "<=*")
364 | (checkargs 2)
365 | (fn [self expression]
366 | (def operator (.pop expression 0))
367 | (def op-id (ast.get-op-id operator))
368 | (def (, exprs ret) (.-compile-collect self expression))
369 | (+ ret (ast.Op op-id
370 | (get exprs 0)
371 | (get exprs 1)))))]
372 |
373 | [compile-unary-operator
374 | (with-decorator
375 | (builds "not" )
376 | (builds "len")
377 | (checkargs 1)
378 | (fn [self expression]
379 | (def operator (.pop expression 0))
380 | (def op-id (ast.get-op-id operator))
381 | (def operand (.compile self (.pop expression 0)))
382 | (+= operand (ast.Op op-id operand.expr))
383 | operand))]
384 |
385 | [compile-binary-operators
386 | (with-decorator
387 | (builds "and")
388 | (builds "or")
389 | (builds "%")
390 | (builds "/")
391 | (builds "//")
392 | (builds "^")
393 | ;; bitwise for lua 5.3
394 | (builds "|")
395 | (builds "bor")
396 | (builds "&")
397 | (builds "<<")
398 | (builds ">>")
399 | (builds "concat")
400 | (fn [self expression]
401 | (def operator (.pop expression 0))
402 | (def op-id (ast.get-op-id operator))
403 |
404 | (def ret (.compile self (.pop expression 0)))
405 |
406 | (for [child expression]
407 | (def left-expr ret.force-expr)
408 | (+= ret (.compile self child))
409 | (def right-expr ret.force-expr)
410 | (+= ret (ast.Op op-id left-expr right-expr)))
411 | (+ ret (ast.Paren ret.expr))))]
412 |
413 | [compile-add-and-mul-expression
414 | (with-decorator
415 | (builds "+")
416 | (builds "*")
417 | (fn [self expression]
418 | (if (> (len expression) 2)
419 | (.compile-binary-operators self expression)
420 | (do
421 | (def id-op {"+" (HyInteger 0) "*" (HyInteger 1)})
422 | (def op (.pop expression 0))
423 | (def arg (if (empty? expression)
424 | (get id-op op)
425 | (.pop expression 0)))
426 | (def expr (.replace (HyExpression [(HySymbol op)
427 | (get id-op op)
428 | arg])
429 | expression))
430 | (.compile-binary-operators self expr)))))]
431 |
432 | [compile-sub-expression
433 | (with-decorator
434 | (builds "-")
435 | (fn [self expression]
436 | (if (> (len expression) 2)
437 | (.compile-binary-operators self expression)
438 | (do
439 | (def arg (get expression 1))
440 | (def ret (.compile self arg))
441 | (+= ret (ast.Op "sub" ret.force-expr))
442 | ret))))]
443 |
444 | [compile-expression
445 | (with-decorator (builds HyExpression)
446 | (fn [self expression]
447 | ;;; FIXME: macroexpand and "." and a lot more
448 | (setv expression (hy.macros.macroexpand expression
449 | self.module-name))
450 | (cond [(not (instance? HyExpression expression))
451 | (.compile self expression)]
452 | [(= expression [])
453 | (.compile-list self expression)]
454 | [true
455 | (let [[fun (get expression 0)]]
456 | (cond [(instance? HyKeyword fun)
457 | (print "FIXME: keyword call")]
458 | [(instance? HyString fun)
459 | (do
460 | (setv ret (.compile-atom self fun expression))
461 | (if (not (nil? ret))
462 | ret
463 | (.-compile-fun-call self expression)))]
464 | [true
465 | (let [[func (.compile self fun)]]
466 | (def (, args ret)
467 | (.-compile-collect self
468 | (slice expression 1)))
469 | (def call (ast.Call func.expr
470 | args))
471 | (+ func ret call))]))])))]
472 |
473 | [-compile-fun-call
474 | (fn [self expression]
475 | (setv fun (get expression 0))
476 | (setv func nil)
477 | (setv method-call? false)
478 |
479 | (when (.startswith fun ".")
480 | (setv method-call? true)
481 | (setv ofun fun)
482 | (setv fun (HySymbol (slice ofun 1)))
483 | (.replace fun ofun)
484 |
485 | (when (< (len expression) 2)
486 | (print "FIXME error message"))
487 |
488 | ;; FIXME: this line should we ensure the compiled result is a string?
489 | (setv method-name (ast.String (ast-str fun)))
490 | (setv func (.compile self (.pop expression 1))))
491 | (when (nil? func)
492 | (setv func (.compile self fun)))
493 |
494 | ;; FIXME: no kwargs for lua?
495 | (setv (, args ret) (.-compile-collect self
496 | (slice expression 1)))
497 |
498 | (setv call (if method-call?
499 | (ast.Invoke func.expr
500 | method-name
501 | args)
502 | (ast.Call func.expr
503 | args)))
504 |
505 | (+ func ret call))]
506 |
507 | [compile-def-expression
508 | (with-decorator
509 | (builds "def")
510 | (checkargs 2)
511 | (fn [self expression]
512 | (.-compile-define self
513 | (get expression 1)
514 | (get expression 2))))]
515 |
516 | [-compile-define
517 | (fn [self name result]
518 | (setv str-name (% "%s" name))
519 |
520 | ;;; FIXME test builtin
521 | (setv result (.compile self result))
522 | (setv ident (.compile self name))
523 |
524 | (if (and (empty? ident.stmts)
525 | (instance? (, ast.Multi ast.Id) ident.expr))
526 | (setv ident ident.expr)
527 | (raise "FIXME: identities required"))
528 |
529 | (if (empty? result.temp-vars)
530 | (+= result (ast.Local ident
531 | result.force-expr))
532 | (.rename result (if (instance? ast.Id ident)
533 | ident.name
534 | (list-comp (. idn name)
535 | [idn ident.nodes]))))
536 |
537 | (+= result ident)
538 | result)]
539 |
540 | [compile-setv-expression
541 | (with-decorator
542 | (builds "setv")
543 | (checkargs 2)
544 | (fn [self expression]
545 | (let [[name (get expression 1)]
546 | [result (get expression 2)]]
547 | (setv result (.compile self result))
548 | (setv ld-name (.compile self name))
549 |
550 | (when (and (instance? ast.Multi ld-name.expr)
551 | (not (empty? result.temp-vars)))
552 | (.rename result
553 | (list-comp (.get-anon-var self)
554 | [i (range (.count ld-name.expr))])))
555 |
556 | ;; FIXME do we need this? (setv st-name (.-storeize self ld-name))
557 |
558 | (setv result (+ result
559 | (ast.Set [ld-name.expr]
560 | [result.force-expr])
561 | ld-name))
562 | result)))]
563 |
564 | [compile-for-expression
565 | (with-decorator
566 | (builds "for*")
567 | (apply checkargs [] {"min" 1})
568 | (fn [self expression]
569 | (.pop expression 0)
570 | (def args (.pop expression 0))
571 | (when (not (instance? HyList args))
572 | (raise (.format "FIXME for expects a list, received `{0}'"
573 | (. (type args) --name--))))
574 | ;; FIXME for args number checkign
575 | (def (, target-name iterable) args)
576 |
577 | (def target (.-storeize self (.compile self target-name)))
578 |
579 | (def body (.-compile-branch self expression))
580 | (+= body (.expr-as-stmt body))
581 |
582 | (def ret (Result))
583 | (+= ret (.compile self iterable))
584 |
585 | ;; two form of for
586 | ;; generic for: (for* [expr iterable] body)
587 | ;; numeric for: (for* [expr [init final step]] body)
588 |
589 | (if (is HyList (type iterable)) ; this looks ugly, but it prevent HyExpression go into the true branch
590 | (+= ret (ast.Fornum target ret.force-expr.nodes body.stmts))
591 | (+= ret (ast.Forin target ret.force-expr body.stmts)))
592 | ret))]
593 |
594 | [compile-integer
595 | (with-decorator (builds HyInteger)
596 | (fn [self number]
597 | (ast.Number number)))]
598 |
599 | [compile-float
600 | (with-decorator (builds HyFloat)
601 | (fn [self number]
602 | (ast.Number number)))]
603 |
604 | [compile-string
605 | (with-decorator (builds HyString)
606 | (fn [self string]
607 | (ast.String string)))]
608 |
609 | [compile-symbol
610 | (with-decorator (builds HySymbol)
611 | (fn [self symbol]
612 | ;;; FIXME more complex case
613 |
614 | (if (in "." symbol)
615 | (do
616 | (setv (, glob local) (.rsplit symbol "." 1))
617 | (setv glob (.replace (HySymbol glob) symbol))
618 | (setv ret (.compile-symbol self glob))
619 | (setv ret (ast.Index ret (ast.String (ast-str local))))
620 | ret)
621 | (cond
622 | [(= symbol "True") (ast.MLTrue)]
623 | [(= symbol "False") (ast.MLFalse)]
624 | [(or (= symbol "None")
625 | (= symbol "nil"))
626 | (ast.Nil)]
627 | [(= symbol "DOTDOTDOT") (ast.Dots)]
628 | [true (ast.Id (ast-str symbol))]))))]
629 |
630 | [compile-list
631 | (with-decorator (builds HyList)
632 | (fn [self expression]
633 | (setv (, elts ret) (.-compile-collect self expression))
634 | (+= ret (ast.Table elts nil))
635 | ret))]
636 |
637 | [compile-function-def
638 | (with-decorator
639 | (builds "lambda")
640 | (builds "fn")
641 | (apply checkargs [] {"min" 1})
642 | (fn [self expression]
643 | (.pop expression 0)
644 | (def arglist (.pop expression 0))
645 | (def (, ret -args defaults vararg)
646 | (.-parse-lambda-list self arglist))
647 | (def args (list-comp (. (.compile self arg) expr)
648 | [arg -args]))
649 | (def body (Result))
650 |
651 | ;; FIXME &optional parameters
652 | ;; use var = var == nil and default_value or var
653 |
654 | (when vararg
655 | (.append args (ast.Dots))
656 | (+= body (apply Result
657 | []
658 | {"stmts" [(ast.Local [(ast.Id vararg)]
659 | [(ast.Table
660 | [(ast.Dots)]
661 | nil)])]})))
662 |
663 | (+= body (.-compile-branch self expression))
664 |
665 | (when body.expr
666 | (+= body (ast.Return body.expr)))
667 |
668 | (+= ret (ast.Function args
669 | body.stmts))
670 | ret))]
671 |
672 | [compile-return
673 | (with-decorator
674 | (builds "return")
675 | (apply checkargs [] {"min" 1})
676 | (fn [self expression]
677 | (.pop expression 0)
678 | (def (, return-exprs ret) (.-compile-collect self expression))
679 | (+ ret (ast.Return return-exprs))))]
680 |
681 | [compile-dispatch-reader-macro
682 | (with-decorator
683 | (builds "dispatch_reader_macro")
684 | (checkargs 2)
685 | (fn [self expression]
686 | (.pop expression 0)
687 | (def str-char (.pop expression 0))
688 | (when (not (type str-char))
689 | (raise "FIXME"))
690 |
691 | (def module self.module-name)
692 | (def expr (hy.macros.reader-macroexpand str-char
693 | (.pop expression 0)
694 | module))
695 | (.compile self expr)))]
696 |
697 | [compile-dict
698 | (with-decorator (builds HyDict)
699 | (fn [self m]
700 | (def (, kv ret) (.-compile-collect self m))
701 | (def length (len kv))
702 | (if (= length 1)
703 | (+= ret (ast.Table kv nil))
704 | (do
705 | (setv half-length (int (/ length 2)))
706 | (setv hash-part (dict-comp (get kv (* 2 i))
707 | (get kv (inc (* 2 i)))
708 | [i (range half-length)]))
709 | (+= ret (ast.Table nil hash-part)) ))
710 |
711 | ret))]])
712 |
713 | (defn compile-file-to-string [filename]
714 | (def metalua-ast (let [[hst (import-file-to-hst filename)]
715 | ;; use filename as module name here since the
716 | ;; only function of module name in hua
717 | ;; compiler is to track which file requires
718 | ;; which macros
719 | [compiler (HuaASTCompiler filename)]]
720 | (.compile compiler hst)))
721 | (def stmts (ast.to-ml-table metalua-ast.stmts))
722 | (tlast->src stmts))
723 |
724 | (defn compile-file [filename]
725 | (def result (compile-file-to-string filename))
726 | (def (, basename extname) (os.path.splitext filename))
727 | (def lua-filename (+ basename ".lua"))
728 | (with [[lua-f (open lua-filename "w")]]
729 | (.write lua-f result)))
730 |
731 |
--------------------------------------------------------------------------------
/hua/core/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/larme/hua/c7462e691fc1e5196fd476b4d75a8a280701190f/hua/core/__init__.py
--------------------------------------------------------------------------------
/hua/core/assignment.hy:
--------------------------------------------------------------------------------
1 | (import [hua.core.utils [hua-gensym simple-form?]])
2 |
3 | (defmacro assoc [table &rest indexes-and-value]
4 | (def last-index (dec (len indexes-and-value)))
5 | (def indexes (slice indexes-and-value
6 | 0
7 | last-index))
8 | (def value (get indexes-and-value last-index))
9 | `(setv (get ~table ~@indexes) ~value))
10 |
11 | (defmacro --hua-augmented-assignment-- [op op-assign]
12 | `(defmacro ~op-assign [target val]
13 | (def op-symbol (quote ~op))
14 | (if (simple-form? target)
15 | `(setv ~target (~op-symbol ~target ~val))
16 | (macro-error target
17 | "Sorry, currently the first argument of augmented assignment can be only a symbol"))))
18 |
19 | (--hua-augmented-assignment-- + +=)
20 | (--hua-augmented-assignment-- - -=)
21 | (--hua-augmented-assignment-- * *=)
22 | (--hua-augmented-assignment-- / /=)
23 |
--------------------------------------------------------------------------------
/hua/core/comp.hy:
--------------------------------------------------------------------------------
1 | (import [hua.core.utils [hua-gensym]])
2 |
3 | (defmacro list-comp [expr gen &optional condition]
4 | (def temp-var-l (hua-gensym "result"))
5 | (def result-assignment
6 | `(assoc ~temp-var-l
7 | (+ 1 (len ~temp-var-l))
8 | ~expr))
9 | (def for-body (if condition
10 | `(when ~condition
11 | ~result-assignment)
12 | `(do
13 | ~result-assignment)))
14 | `(let [[~temp-var-l []]]
15 | (for ~gen
16 | ~for-body)
17 | ~temp-var-l))
18 |
19 | (defmacro dict-comp [key-expr value-expr gen &optional condition]
20 | (def temp-var-l (hua-gensym "result"))
21 | (def result-assignment
22 | `(assoc ~temp-var-l
23 | ~key-expr
24 | ~value-expr))
25 | (def for-body (if condition
26 | `(when ~condition
27 | ~result-assignment)
28 | `(do
29 | ~result-assignment)))
30 | `(let [[~temp-var-l []]]
31 | (for ~gen
32 | ~for-body)
33 | ~temp-var-l))
34 |
--------------------------------------------------------------------------------
/hua/core/hua_stdlib.hua:
--------------------------------------------------------------------------------
1 | (require-macro hua.core.macros)
2 | (require-macro hua.core.assignment)
3 | (require-macro hua.core.module)
4 |
5 | (hua-import [moses [clone
6 | isArray
7 | isTable
8 | isEqual
9 | isCallable
10 | isIterable
11 | isString
12 | isFunction
13 | isNil
14 | isNumber
15 | isNaN
16 | isFinite
17 | isBoolean
18 | isInteger]])
19 |
20 | (defn apply [f args]
21 | (f (unpack args)))
22 |
23 | (defn dec [n]
24 | (- n 1))
25 |
26 | (defn first [tbl]
27 | (get tbl 1))
28 |
29 | (defn inc [n]
30 | (+ n 1))
31 |
32 |
33 | ;;; core functions from moses
34 |
35 | (def array? isArray)
36 | (def table? isTable)
37 | (def equal? isEqual)
38 | (def callable? isCallable)
39 | (def iterable? isIterable)
40 | (def string? isString)
41 | (def function? isFunction)
42 | (def nil? isNil)
43 | (def number? isNumber)
44 | (def nan? isNaN)
45 | (def finite? isFinite)
46 | (def boolean? isBoolean)
47 | (def int? isInteger)
48 |
49 | (export apply
50 | dec
51 | first
52 | inc
53 |
54 | ;; moses functions
55 | clone
56 | array?
57 | table?
58 | equal?
59 | callable?
60 | iterable?
61 | string?
62 | function?
63 | nil?
64 | number?
65 | nan?
66 | finite?
67 | boolean?
68 | int?
69 | )
70 |
--------------------------------------------------------------------------------
/hua/core/hua_stdlib.lua:
--------------------------------------------------------------------------------
1 | local clone = nil
2 | local isArray = nil
3 | local isTable = nil
4 | local isEqual = nil
5 | local isCallable = nil
6 | local isIterable = nil
7 | local isString = nil
8 | local isFunction = nil
9 | local isNil = nil
10 | local isNumber = nil
11 | local isNaN = nil
12 | local isFinite = nil
13 | local isBoolean = nil
14 | local isInteger = nil
15 | local _hua_anon_var_1
16 | do
17 | local __hua_import_m__ = require("moses")
18 | clone = __hua_import_m__["clone"]
19 | isArray = __hua_import_m__["isArray"]
20 | isTable = __hua_import_m__["isTable"]
21 | isEqual = __hua_import_m__["isEqual"]
22 | isCallable = __hua_import_m__["isCallable"]
23 | isIterable = __hua_import_m__["isIterable"]
24 | isString = __hua_import_m__["isString"]
25 | isFunction = __hua_import_m__["isFunction"]
26 | isNil = __hua_import_m__["isNil"]
27 | isNumber = __hua_import_m__["isNumber"]
28 | isNaN = __hua_import_m__["isNaN"]
29 | isFinite = __hua_import_m__["isFinite"]
30 | isBoolean = __hua_import_m__["isBoolean"]
31 | isInteger = __hua_import_m__["isInteger"]
32 | _hua_anon_var_1 = isInteger
33 | end
34 | local apply = nil
35 | apply = function (f, args)
36 | return f(unpack(args))
37 | end
38 | local dec = nil
39 | dec = function (n)
40 | return (n - 1)
41 | end
42 | local first = nil
43 | first = function (tbl)
44 | return tbl[1]
45 | end
46 | local inc = nil
47 | inc = function (n)
48 | return (n + 1)
49 | end
50 | local is_array = isArray
51 | local is_table = isTable
52 | local is_equal = isEqual
53 | local is_callable = isCallable
54 | local is_iterable = isIterable
55 | local is_string = isString
56 | local is_function = isFunction
57 | local is_nil = isNil
58 | local is_number = isNumber
59 | local is_nan = isNaN
60 | local is_finite = isFinite
61 | local is_boolean = isBoolean
62 | local is_int = isInteger
63 | local _hua_anon_var_2
64 | do
65 | local _hua_module_1235 = {}
66 | _hua_module_1235["apply"] = apply
67 | _hua_module_1235["dec"] = dec
68 | _hua_module_1235["first"] = first
69 | _hua_module_1235["inc"] = inc
70 | _hua_module_1235["clone"] = clone
71 | _hua_module_1235["is_array"] = is_array
72 | _hua_module_1235["is_table"] = is_table
73 | _hua_module_1235["is_equal"] = is_equal
74 | _hua_module_1235["is_callable"] = is_callable
75 | _hua_module_1235["is_iterable"] = is_iterable
76 | _hua_module_1235["is_string"] = is_string
77 | _hua_module_1235["is_function"] = is_function
78 | _hua_module_1235["is_nil"] = is_nil
79 | _hua_module_1235["is_number"] = is_number
80 | _hua_module_1235["is_nan"] = is_nan
81 | _hua_module_1235["is_finite"] = is_finite
82 | _hua_module_1235["is_boolean"] = is_boolean
83 | _hua_module_1235["is_int"] = is_int
84 | return _hua_module_1235
85 | end
86 |
--------------------------------------------------------------------------------
/hua/core/initialize.hy:
--------------------------------------------------------------------------------
1 | (import os)
2 | (import [hy.models.string [HyString]])
3 |
4 | ;;; In case the future lua release removes the unpack function entirely
5 |
6 | (defmacro --hua-def-unpack-- []
7 | `(def unpack (or unpack table.unpack)))
8 |
9 |
10 | ;;; add the current path in the lua load path
11 |
12 | (defmacro --hua-add-stdlib-path-- []
13 | (def this-path
14 | (HyString
15 | (os.path.dirname
16 | (os.path.realpath --file--))))
17 | `(setv package.path
18 | (string.format "%s;%s/?.lua"
19 | package.path
20 | ~this-path)))
21 |
22 | ;;; import standard library
23 | (defmacro --hua-import-stdlib-- []
24 | `(hua-import [hua_stdlib [apply
25 | dec
26 | first
27 | inc
28 |
29 | ;; moses functions
30 | clone
31 | array?
32 | table?
33 | equal?
34 | callable?
35 | iterable?
36 | string?
37 | function?
38 | nil?
39 | number?
40 | nan?
41 | finite?
42 | boolean?
43 | int?]]))
44 |
45 | ;;; a macro to import all core macros
46 |
47 | (defmacro --hua-init-macros-- []
48 | `(do
49 | (require-macro hua.core.macros)
50 | (require-macro hua.core.op)
51 | (require-macro hua.core.assignment)
52 | (require-macro hua.core.comp)
53 | (require-macro hua.core.oo)
54 | (require-macro hua.core.module)))
55 |
56 |
57 | (defmacro --hua-initialize-- []
58 | `(do
59 | (--hua-add-stdlib-path--)
60 | (--hua-def-unpack--)
61 | (--hua-init-macros--)
62 | (--hua-import-stdlib--)))
63 |
--------------------------------------------------------------------------------
/hua/core/macros.hy:
--------------------------------------------------------------------------------
1 | (import [hy.models.list [HyList]]
2 | [hy.models.symbol [HySymbol]])
3 |
4 | (defn for-helper [body]
5 | (defn for-helper* [args-iter]
6 | (try
7 | `(for* [~(next args-iter)
8 | ~(next args-iter)]
9 | ~(for-helper* args-iter))
10 | (catch [e StopIteration]
11 | `(progn ~@body)))))
12 |
13 | (defmacro for [args &rest body]
14 | "shorthand for nested for loops:
15 | (for [x foo
16 | y bar]
17 | baz) ->
18 | (for* [x foo]
19 | (for* [y bar]
20 | baz))"
21 | (cond
22 | [(odd? (len args))
23 | (macro-error args "`for' requires an even number of args.")]
24 | [(empty? body)
25 | (macro-error None "`for' requires a body to evaluate")]
26 | [(empty? args) `(do ~@body)]
27 | [(= (len args) 2) `(for* [~@args] ~@body)]
28 | [true
29 | (do
30 | (def args-iter (iter args))
31 | ((for-helper body) args-iter))]))
32 |
33 | (defmacro let [variables &rest body]
34 | "Execute `body` in the lexical context of `variables`"
35 | (def macroed-variables [])
36 | (if (not (isinstance variables HyList))
37 | (macro-error variables "let lexical context must be a list"))
38 | (for* [variable variables]
39 | (if (isinstance variable HyList)
40 | (do
41 | (if (!= (len variable) 2)
42 | (macro-error variable "let variable assignments must contain two items"))
43 | (.append macroed-variables `(def ~(get variable 0) ~(get variable 1))))
44 | (if (isinstance variable HySymbol)
45 | (.append macroed-variables `(def ~variable None))
46 | (macro-error variable "let lexical context element must be a list or symbol"))))
47 | `(do-block ~@macroed-variables
48 | ~@body))
49 |
50 |
51 | (defmacro-alias [defn defun] [name lambda-list &rest body]
52 | "define a function `name` with signature `lambda-list` and body `body`"
53 | (if (not (= (type name) HySymbol))
54 | (macro-error name "defn/defun takes a name as first argument"))
55 | (if (not (isinstance lambda-list HyList))
56 | (macro-error name "defn/defun takes a parameter list as second argument"))
57 | `(do
58 | (def ~name nil)
59 | (setv ~name (fn ~lambda-list ~@body))))
60 |
61 |
--------------------------------------------------------------------------------
/hua/core/module.hy:
--------------------------------------------------------------------------------
1 | (import [hy.models.list [HyList]]
2 | [hy.models.string [HyString]]
3 | [hy.models.symbol [HySymbol]]
4 | [hua.core.utils [hua-gensym]])
5 |
6 | (defn import-helper [item]
7 | (when (not (instance? (, HySymbol HyList) item))
8 | (macro-error item "(import) requires a Symbol or a List"))
9 |
10 | (if (instance? HySymbol item)
11 | `(def ~item (require ~(HyString item)))
12 | (cond [(= 2 (len item))
13 | (let [[module (get item 0)]
14 | [syms (get item 1)]
15 | [declares (list-comp `(def ~sym nil)
16 | [sym syms])]
17 | [bindings (list-comp
18 | `(setv ~sym
19 | (get --hua-import-m--
20 | ~(HyString sym)))
21 | [sym syms])]]
22 | `(do
23 | ~@declares
24 | (let [[--hua-import-m-- (require ~(HyString module))]]
25 | ~@bindings)))]
26 | [(and (= 3 (len item))
27 | (= (get item 1) :as))
28 | (let [[module (get item 0)]
29 | [alias (get item 2)]]
30 | `(def ~alias (require ~(HyString module))))]
31 | [true
32 | (macro-error item
33 | "When the argument of (import) is a List, it need to be in one of the two following form: 1. [module [var1 var2 ...]] or 2. [module :as alias]")])))
34 |
35 | ;;; well, before we have a way to name it import
36 | (defmacro hua-import [&rest items]
37 | (def body (list-comp (import-helper item) [item items]))
38 | `(do ~@body))
39 |
40 |
41 | ;;; export a list o variables, use at the end of a hua file
42 | (defmacro export [&rest vars]
43 | (def module-var (hua-gensym "module"))
44 | (def assignments (list-comp `(assoc ~module-var
45 | ~(HyString var)
46 | ~var)
47 | [var vars]))
48 | `(let [[~module-var {}]]
49 | (do ~@assignments)
50 | (return ~module-var)))
51 |
--------------------------------------------------------------------------------
/hua/core/moses.lua:
--------------------------------------------------------------------------------
1 | --- *Utility-belt library for functional programming in Lua.*
2 | -- Source on [Github](http://github.com/Yonaba/Moses)
3 | -- @author [Roland Yonaba](http://github.com/Yonaba)
4 | -- @copyright 2012-2014
5 | -- @license [MIT](http://www.opensource.org/licenses/mit-license.php)
6 | -- @release 1.4.0
7 | -- @module moses
8 |
9 | local _MODULEVERSION = '1.4.0'
10 |
11 | -- Internalisation
12 | local next, type, unpack, select, pcall = next, type, unpack, select, pcall
13 | local setmetatable, getmetatable = setmetatable, getmetatable
14 | local t_insert, t_sort = table.insert, table.sort
15 | local t_remove,t_concat = table.remove, table.concat
16 | local randomseed, random, huge = math.randomseed, math.random, math.huge
17 | local floor, max, min = math.floor, math.max, math.min
18 | local rawget = rawget
19 | local unpack = unpack
20 | local pairs,ipairs = pairs,ipairs
21 | local _ = {}
22 |
23 |
24 | -- ======== Private helpers
25 |
26 | local function f_max(a,b) return a>b end
27 | local function f_min(a,b) return ab and b or var) end
29 | local function isTrue(_,value) return value and true end
30 | local function iNot(value) return not value end
31 |
32 | local function count(t) -- raw count of items in an map-table
33 | local i = 0
34 | for k,v in pairs(t) do i = i + 1 end
35 | return i
36 | end
37 |
38 | local function extract(list,comp,transform,...) -- extracts value from a list
39 | local _ans
40 | local transform = transform or _.identity
41 | for index,value in pairs(list) do
42 | if not _ans then _ans = transform(value,...)
43 | else
44 | local value = transform(value,...)
45 | _ans = comp(_ans,value) and _ans or value
46 | end
47 | end
48 | return _ans
49 | end
50 |
51 | local function partgen(t, n, f) -- generates array partitions
52 | for i = 0, #t, n do
53 | local s = _.slice(t, i+1, i+n)
54 | if #s>0 then f(s) end
55 | end
56 | end
57 |
58 | local function permgen(t, n, f) -- taken from PiL: http://www.lua.org/pil/9.3.html
59 | if n == 0 then f(t) end
60 | for i = 1,n do
61 | t[n], t[i] = t[i], t[n]
62 | permgen(t, n-1, f)
63 | t[n], t[i] = t[i], t[n]
64 | end
65 | end
66 |
67 | -- Internal counter for unique ids generation
68 | local unique_id_counter = -1
69 |
70 | --- Table functions
71 | -- @section Table functions
72 |
73 | --- Iterates on each key-value pairs in a table. Calls function `f(key, value)` at each step of iteration.
74 | --
Aliased as `forEach`.
75 | -- @name each
76 | -- @tparam table t a table
77 | -- @tparam function f an iterator function, prototyped as `f(key, value, ...)`
78 | -- @tparam[opt] vararg ... Optional extra-args to be passed to function `f`
79 | -- @see eachi
80 | function _.each(t, f, ...)
81 | for index,value in pairs(t) do
82 | f(index,value,...)
83 | end
84 | end
85 |
86 | --- Iterates on each integer key-value pairs in a table. Calls function `f(key, value)`
87 | -- only on values at integer key in a given collection. The table can be a sparse array,
88 | -- or map-like. Iteration will start from the lowest integer key found to the highest one.
89 | --
Aliased as `forEachi`.
90 | -- @name eachi
91 | -- @tparam table t a table
92 | -- @tparam function f an iterator function, prototyped as `f(key, value, ...)`
93 | -- @tparam[opt] vararg ... Optional extra-args to be passed to function `f`
94 | -- @see each
95 | function _.eachi(t, f, ...)
96 | local lkeys = _.sort(_.select(_.keys(t), function(k,v)
97 | return _.isInteger(v)
98 | end))
99 | for k, key in ipairs(lkeys) do
100 | f(key, t[key],...)
101 | end
102 | end
103 |
104 | --- Returns an array of values at specific indexes and keys.
105 | -- @name at
106 | -- @tparam table t a table
107 | -- @tparam vararg ... A variable number of indexes or keys to extract values
108 | -- @treturn table an array-list of values from the passed-in table
109 | function _.at(t, ...)
110 | local values = {}
111 | for i, key in ipairs({...}) do
112 | if _.has(t, key) then values[#values+1] = t[key] end
113 | end
114 | return values
115 | end
116 |
117 | --- Counts occurrences of a given value in a table. Uses @{isEqual} to compare values.
118 | -- @name count
119 | -- @tparam table t a table
120 | -- @tparam[opt] value value a value to be searched in the table. If not given, the @{size} of the table will be returned
121 | -- @treturn number the count of occurrences of `value`
122 | -- @see countf
123 | -- @see size
124 | function _.count(t, value)
125 | if _.isNil(value) then return _.size(t) end
126 | local count = 0
127 | _.each(t, function(k,v)
128 | if _.isEqual(v, value) then count = count + 1 end
129 | end)
130 | return count
131 | end
132 |
133 | --- Counts occurrences validating a predicate. Same as @{count}, but uses an iterator.
134 | -- Returns the count for values passing the test `f(key, value, ...)`
135 | -- @name countf
136 | -- @tparam table t a table
137 | -- @tparam function f an iterator function, prototyped as `f(key, value, ...)`
138 | -- @tparam[opt] vararg ... Optional extra-args to be passed to function `f`
139 | -- @treturn number the count of values validating the predicate
140 | -- @see count
141 | -- @see size
142 | function _.countf(t, f, ...)
143 | return _.count(_.map(t, f, ...), true)
144 | end
145 |
146 | --- Iterates through a table and loops `n` times. The full iteration loop will be
147 | -- repeated `n` times (or forever, if `n` is omitted). In case `n` is lower or equal to 0, it returns
148 | -- an empty function.
149 | --
Aliased as `loop`.
150 | -- @name cycle
151 | -- @tparam table t a table
152 | -- @tparam number n the number of loops
153 | -- @treturn function an iterator function yielding key-value pairs from the passed-in table.
154 | function _.cycle(t, n)
155 | n = n or 1
156 | if n<=0 then return function() end end
157 | local k, fk
158 | local i = 0
159 | while true do
160 | return function()
161 | k = k and next(t,k) or next(t)
162 | fk = not fk and k or fk
163 | if n then
164 | i = (k==fk) and i+1 or i
165 | if i > n then
166 | return
167 | end
168 | end
169 | return k, t[k]
170 | end
171 | end
172 | end
173 |
174 | --- Maps function `f(key, value)` on all key-value pairs. Collects
175 | -- and returns the results as a table.
176 | --
Aliased as `collect`.
177 | -- @name map
178 | -- @tparam table t a table
179 | -- @tparam function f an iterator function, prototyped as `f(key, value, ...)`
180 | -- @tparam[opt] vararg ... Optional extra-args to be passed to function `f`
181 | -- @treturn table a table of results
182 | function _.map(t, f, ...)
183 | local _t = {}
184 | for index,value in pairs(t) do
185 | _t[index] = f(index,value,...)
186 | end
187 | return _t
188 | end
189 |
190 | --- Reduces a table, left-to-right. Folds the table from the first element to the last element
191 | -- to into a single value, with respect to a given iterator and an initial state.
192 | -- The given function takes a state and a value and returns a new state.
193 | --
Aliased as `inject`, `foldl`.
194 | -- @name reduce
195 | -- @tparam table t a table
196 | -- @tparam function f an iterator function, prototyped as `f(state, value)`
197 | -- @tparam[opt] state state an initial state of reduction. Defaults to the first value in the table.
198 | -- @treturn state state the final state of reduction
199 | -- @see reduceRight
200 | function _.reduce(t, f, state)
201 | for __,value in pairs(t) do
202 | if state == nil then state = value
203 | else state = f(state,value)
204 | end
205 | end
206 | return state
207 | end
208 |
209 | --- Reduces a table, right-to-left. Folds the table from the last element to the first element
210 | -- to single value, with respect to a given iterator and an initial state.
211 | -- The given function takes a state and a value, and returns a new state.
212 | --
Aliased as `injectr`, `foldr`.
213 | -- @name reduceRight
214 | -- @tparam table t a table
215 | -- @tparam function f an iterator function, prototyped as `f(state,value)`
216 | -- @tparam[opt] state state an initial state of reduction. Defaults to the last value in the table.
217 | -- @treturn state state the final state of reduction
218 | -- @see reduce
219 | function _.reduceRight(t, f, state)
220 | return _.reduce(_.reverse(t),f,state)
221 | end
222 |
223 | --- Reduces a table while saving intermediate states. Folds the table left-to-right
224 | -- to a single value, with respect to a given iterator and an initial state. The given function
225 | -- takes a state and a value, and returns a new state. It returns an array of intermediate states.
226 | --
Aliased as `mapr`
227 | -- @name mapReduce
228 | -- @tparam table t a table
229 | -- @tparam function f an iterator function, prototyped as `f(state, value)`
230 | -- @tparam[opt] state state an initial state of reduction. Defaults to the first value in the table.
231 | -- @treturn table an array of states
232 | -- @see mapReduceRight
233 | function _.mapReduce(t, f, state)
234 | local _t = {}
235 | for i,value in pairs(t) do
236 | _t[i] = not state and value or f(state,value)
237 | state = _t[i]
238 | end
239 | return _t
240 | end
241 |
242 | --- Reduces a table while saving intermediate states. Folds the table right-to-left
243 | -- to a single value, with respect to a given iterator and an initial state. The given function
244 | -- takes a state and a value, and returns a new state. It returns an array of intermediate states.
245 | --
Aliased as `maprr`
246 | -- @name mapReduceRight
247 | -- @tparam table t a table
248 | -- @tparam function f an iterator function, prototyped as `f(state,value)`
249 | -- @tparam[opt] state state an initial state of reduction. Defaults to the last value in the table.
250 | -- @treturn table an array of states
251 | -- @see mapReduce
252 | function _.mapReduceRight(t, f, state)
253 | return _.mapReduce(_.reverse(t),f,state)
254 | end
255 |
256 | --- Search for a value in a table. It does not search in nested tables.
257 | --
Aliased as `any`, `some`
258 | -- @name include
259 | -- @tparam table t a table
260 | -- @tparam value|function value a value to search for
261 | -- @treturn boolean a boolean : `true` when found, `false` otherwise
262 | -- @see detect
263 | -- @see contains
264 | function _.include(t,value)
265 | local _iter = _.isFunction(value) and value or _.isEqual
266 | for __,v in pairs(t) do
267 | if _iter(v,value) then return true end
268 | end
269 | return false
270 | end
271 |
272 | --- Search for a value in a table. Returns the key of the value if found.
273 | -- It does not search in nested tables.
274 | -- @name detect
275 | -- @tparam table t a table
276 | -- @tparam value value a value to search for
277 | -- @treturn key the value key or __nil__
278 | -- @see include
279 | -- @see contains
280 | function _.detect(t, value)
281 | local _iter = _.isFunction(value) and value or _.isEqual
282 | for key,arg in pairs(t) do
283 | if _iter(arg,value) then return key end
284 | end
285 | end
286 |
287 | --- Checks if a value is present in a table.
288 | -- @name contains
289 | -- @tparam table t a table
290 | -- @tparam value value a value to search for
291 | -- @treturn boolean true if present, otherwise false
292 | -- @see include
293 | -- @see detect
294 | function _.contains(t, value)
295 | return _.toBoolean(_.detect(t, value))
296 | end
297 |
298 | --- Returns the first value having specified keys `props`.
299 | -- @function findWhere
300 | -- @tparam table t a table
301 | -- @tparam table props a set of keys
302 | -- @treturn value a value from the passed-in table
303 | function _.findWhere(t, props)
304 | local index = _.detect(t, function(v)
305 | for key in pairs(props) do
306 | if props[key] ~= v[key] then return false end
307 | end
308 | return true
309 | end)
310 | return index and t[index]
311 | end
312 |
313 | --- Selects and extracts values passing an iterator test.
314 | --
Aliased as `filter`.
315 | -- @name select
316 | -- @tparam table t a table
317 | -- @tparam function f an iterator function, prototyped as `f(key, value, ...)`
318 | -- @tparam[opt] vararg ... Optional extra-args to be passed to function `f`
319 | -- @treturn table the selected values
320 | -- @see reject
321 | function _.select(t, f, ...)
322 | local _mapped = _.map(t, f, ...)
323 | local _t = {}
324 | for index,value in pairs(_mapped) do
325 | if value then _t[#_t+1] = t[index] end
326 | end
327 | return _t
328 | end
329 |
330 | --- Clones a table while dropping values passing an iterator test.
331 | --
Aliased as `discard`
332 | -- @name reject
333 | -- @tparam table t a table
334 | -- @tparam function f an iterator function, prototyped as `f(key, value, ...)`
335 | -- @tparam[opt] vararg ... Optional extra-args to be passed to function `f`
336 | -- @treturn table the remaining values
337 | -- @see select
338 | function _.reject(t, f, ...)
339 | local _mapped = _.map(t,f,...)
340 | local _t = {}
341 | for index,value in pairs (_mapped) do
342 | if not value then _t[#_t+1] = t[index] end
343 | end
344 | return _t
345 | end
346 |
347 | --- Checks if all values in a table are passing an iterator test.
348 | --
Aliased as `every`
349 | -- @name all
350 | -- @tparam table t a table
351 | -- @tparam function f an iterator function, prototyped as `f(key, value, ...)`
352 | -- @tparam[opt] vararg ... Optional extra-args to be passed to function `f`
353 | -- @treturn boolean `true` if all values passes the predicate, `false` otherwise
354 | function _.all(t, f, ...)
355 | return ((#_.select(_.map(t,f,...), isTrue)) == (#t))
356 | end
357 |
358 | --- Invokes a method on each value in a table.
359 | -- @name invoke
360 | -- @tparam table t a table
361 | -- @tparam function method a function, prototyped as `f(value, ...)`
362 | -- @tparam[opt] vararg ... Optional extra-args to be passed to function `method`
363 | -- @treturn result the result(s) of method call `f(value, ...)`
364 | -- @see pluck
365 | function _.invoke(t, method, ...)
366 | local args = {...}
367 | return _.map(t, function(__,v)
368 | if _.isTable(v) then
369 | if _.has(v,method) then
370 | if _.isCallable(v[method]) then
371 | return v[method](v,unpack(args))
372 | else
373 | return v[method]
374 | end
375 | else
376 | if _.isCallable(method) then
377 | return method(v,unpack(args))
378 | end
379 | end
380 | elseif _.isCallable(method) then
381 | return method(v,unpack(args))
382 | end
383 | end)
384 | end
385 |
386 | --- Extracts property-values from a table of values.
387 | -- @name pluck
388 | -- @tparam table t a table
389 | -- @tparam string a property, will be used to index in each value: `value[property]`
390 | -- @treturn table an array of values for the specified property
391 | function _.pluck(t, property)
392 | return _.reject(_.map(t,function(__,value)
393 | return value[property]
394 | end), iNot)
395 | end
396 |
397 | --- Returns the max value in a collection. If an transformation function is passed, it will
398 | -- be used to extract the value by which all objects will be sorted.
399 | -- @name max
400 | -- @tparam table t a table
401 | -- @tparam[opt] function transform an transformation function, prototyped as `transform(value,...)`, defaults to @{identity}
402 | -- @tparam[optchain] vararg ... Optional extra-args to be passed to function `transform`
403 | -- @treturn value the maximum value found
404 | -- @see min
405 | function _.max(t, transform, ...)
406 | return extract(t, f_max, transform, ...)
407 | end
408 |
409 | --- Returns the min value in a collection. If an transformation function is passed, it will
410 | -- be used to extract the value by which all objects will be sorted.
411 | -- @name min
412 | -- @tparam table t a table
413 | -- @tparam[opt] function transform an transformation function, prototyped as `transform(value,...)`, defaults to @{identity}
414 | -- @tparam[optchain] vararg ... Optional extra-args to be passed to function `transform`
415 | -- @treturn value the minimum value found
416 | -- @see max
417 | function _.min(t, transform, ...)
418 | return extract(t, f_min, transform, ...)
419 | end
420 |
421 | --- Returns a shuffled copy of a given collection. If a seed is provided, it will
422 | -- be used to init the random number generator (via `math.randomseed`).
423 | -- @name shuffle
424 | -- @tparam table t a table
425 | -- @tparam[opt] number seed a seed
426 | -- @treturn table a shuffled copy of the given table
427 | function _.shuffle(t, seed)
428 | if seed then randomseed(seed) end
429 | local _shuffled = {}
430 | _.each(t,function(index,value)
431 | local randPos = floor(random()*index)+1
432 | _shuffled[index] = _shuffled[randPos]
433 | _shuffled[randPos] = value
434 | end)
435 | return _shuffled
436 | end
437 |
438 | --- Checks if two tables are the same. It compares if both tables features the same values,
439 | -- but not necessarily at the same keys.
440 | -- @name same
441 | -- @tparam table a a table
442 | -- @tparam table b another table
443 | -- @treturn boolean `true` or `false`
444 | function _.same(a, b)
445 | return _.all(a, function (i,v) return _.include(b,v) end)
446 | and _.all(b, function (i,v) return _.include(a,v) end)
447 | end
448 |
449 | --- Sorts a table, in-place. If a comparison function is given, it will be used to sort values.
450 | -- @name sort
451 | -- @tparam table t a table
452 | -- @tparam[opt] function comp a comparison function prototyped as `comp(a,b)`, defaults to < operator.
453 | -- @treturn table the given table, sorted.
454 | function _.sort(t, comp)
455 | t_sort(t, comp)
456 | return t
457 | end
458 |
459 | --- Splits a table into subsets. Each subset feature values from the original table grouped
460 | -- by the result of passing it through an iterator.
461 | -- @name groupBy
462 | -- @tparam table t a table
463 | -- @tparam function iter an iterator function, prototyped as `iter(key, value, ...)`
464 | -- @tparam[opt] vararg ... Optional extra-args to be passed to function `iter`
465 | -- @treturn table a new table with values grouped by subsets
466 | function _.groupBy(t, iter, ...)
467 | local vararg = {...}
468 | local _t = {}
469 | _.each(t, function(i,v)
470 | local _key = iter(i,v, unpack(vararg))
471 | if _t[_key] then _t[_key][#_t[_key]+1] = v
472 | else _t[_key] = {v}
473 | end
474 | end)
475 | return _t
476 | end
477 |
478 | --- Groups values in a collection and counts them.
479 | -- @name countBy
480 | -- @tparam table t a table
481 | -- @tparam function iter an iterator function, prototyped as `iter(key, value, ...)`
482 | -- @tparam[opt] vararg ... Optional extra-args to be passed to function `iter`
483 | -- @treturn table a new table with subsets names paired with their count
484 | function _.countBy(t, iter, ...)
485 | local vararg = {...}
486 | local stats = {}
487 | _.each(t,function(i,v)
488 | local key = iter(i,v,unpack(vararg))
489 | stats[key] = (stats[key] or 0) +1
490 | end)
491 | return stats
492 | end
493 |
494 | --- Counts the number of values in a collection. If being passed more than one args
495 | -- it will return the count of all passed-in args.
496 | -- @name size
497 | -- @tparam[opt] vararg ... Optional variable number of arguments
498 | -- @treturn number a count
499 | -- @see count
500 | -- @see countf
501 | function _.size(...)
502 | local args = {...}
503 | local arg1 = args[1]
504 | if _.isNil(arg1) then
505 | return 0
506 | elseif _.isTable(arg1) then
507 | return count(args[1])
508 | else
509 | return count(args)
510 | end
511 | end
512 |
513 | --- Checks if all the keys of `other` table exists in table `t`. It does not
514 | -- compares values. The test is not commutative, i.e table `t` may contains keys
515 | -- not existing in `other`.
516 | -- @name containsKeys
517 | -- @tparam table t a table
518 | -- @tparam table other another table
519 | -- @treturn boolean `true` or `false`
520 | -- @see sameKeys
521 | function _.containsKeys(t, other)
522 | for key in pairs(other) do
523 | if not t[key] then return false end
524 | end
525 | return true
526 | end
527 |
528 | --- Checks if both given tables have the same keys. It does not compares values.
529 | -- @name sameKeys
530 | -- @tparam table tA a table
531 | -- @tparam table tB another table
532 | -- @treturn boolean `true` or `false`
533 | -- @see containsKeys
534 | function _.sameKeys(tA, tB)
535 | for key in pairs(tA) do
536 | if not tB[key] then return false end
537 | end
538 | for key in pairs(tB) do
539 | if not tA[key] then return false end
540 | end
541 | return true
542 | end
543 |
544 |
545 | --- Array functions
546 | -- @section Array functions
547 |
548 | --- Converts a vararg list to an array-list.
549 | -- @name toArray
550 | -- @tparam[opt] vararg ... Optional variable number of arguments
551 | -- @treturn table an array-list of all passed-in args
552 | function _.toArray(...) return {...} end
553 |
554 | --- Looks for the first occurrence of a given value in an array. Returns the value index if found.
555 | -- @name find
556 | -- @tparam table array an array of values
557 | -- @tparam value value a value to search for
558 | -- @tparam[opt] number from the index from where to start the search. Defaults to 1.
559 | -- @treturn number|nil the index of the value if found in the array, `nil` otherwise.
560 | function _.find(array, value, from)
561 | for i = from or 1, #array do
562 | if _.isEqual(array[i], value) then return i end
563 | end
564 | end
565 |
566 | --- Reverses values in a given array. The passed-in array should not be sparse.
567 | -- @name reverse
568 | -- @tparam table array an array
569 | -- @treturn table a copy of the given array, reversed
570 | function _.reverse(array)
571 | local _array = {}
572 | for i = #array,1,-1 do
573 | _array[#_array+1] = array[i]
574 | end
575 | return _array
576 | end
577 |
578 | --- Collects values from a given array. The passed-in array should not be sparse.
579 | -- This function collects values as long as they satisfy a given predicate.
580 | -- Therefore, it returns on the first falsy test.
581 | --
Aliased as `takeWhile`
582 | -- @name selectWhile
583 | -- @tparam table array an array
584 | -- @tparam function f an iterator function prototyped as `f(key, value, ...)`
585 | -- @tparam[opt] vararg ... Optional extra-args to be passed to function `f`
586 | -- @treturn table a new table containing all values collected
587 | -- @see dropWhile
588 | function _.selectWhile(array, f, ...)
589 | local t = {}
590 | for i,v in ipairs(array) do
591 | if f(i,v,...) then t[i] = v else break end
592 | end
593 | return t
594 | end
595 |
596 | --- Collects values from a given array. The passed-in array should not be sparse.
597 | -- This function collects values as long as they do not satisfy a given predicate.
598 | -- Therefore it returns on the first true test.
599 | --
Aliased as `rejectWhile`
600 | -- @name dropWhile
601 | -- @tparam table array an array
602 | -- @tparam function f an iterator function prototyped as `f(key,value,...)`
603 | -- @tparam[opt] vararg ... Optional extra-args to be passed to function `f`
604 | -- @treturn table a new table containing all values collected
605 | -- @selectWhile
606 | function _.dropWhile(array, f, ...)
607 | local _i
608 | for i,v in ipairs(array) do
609 | if not f(i,v,...) then
610 | _i = i
611 | break
612 | end
613 | end
614 | if _.isNil(_i) then return {} end
615 | return _.rest(array,_i)
616 | end
617 |
618 | --- Returns the index at which a value should be inserted. This returned index is determined so
619 | -- that it maintains the sort. If a comparison function is passed, it will be used to sort all
620 | -- values.
621 | -- @name sortedIndex
622 | -- @tparam table array an array
623 | -- @tparam value the value to be inserted
624 | -- @tparam[opt] function comp an comparison function prototyped as `f(a, b)`, defaults to < operator.
625 | -- @tparam[optchain] boolean sort whether or not the passed-in array should be sorted
626 | -- @treturn number the index at which the passed-in value should be inserted
627 | function _.sortedIndex(array, value, comp, sort)
628 | local _comp = comp or f_min
629 | if sort then _.sort(array,_comp) end
630 | for i = 1,#array do
631 | if not _comp(array[i],value) then return i end
632 | end
633 | return #array+1
634 | end
635 |
636 | --- Returns the index of a given value in an array. If the passed-in value exists
637 | -- more than once in the array, it will return the index of the first occurrence.
638 | -- @name indexOf
639 | -- @tparam table array an array
640 | -- @tparam value the value to search for
641 | -- @treturn number|nil the index of the passed-in value
642 | -- @see lastIndexOf
643 | function _.indexOf(array, value)
644 | for k = 1,#array do
645 | if array[k] == value then return k end
646 | end
647 | end
648 |
649 | --- Returns the index of the last occurrence of a given value.
650 | -- @name lastIndexOf
651 | -- @tparam table array an array
652 | -- @tparam value the value to search for
653 | -- @treturn number|nil the index of the last occurrence of the passed-in value or __nil__
654 | -- @see indexOf
655 | function _.lastIndexOf(array, value)
656 | local key = _.indexOf(_.reverse(array),value)
657 | if key then return #array-key+1 end
658 | end
659 |
660 | --- Adds all passed-in values at the top of an array. The last arguments will bubble to the
661 | -- top of the given array.
662 | -- @name addTop
663 | -- @tparam table array an array
664 | -- @tparam vararg ... a variable number of arguments
665 | -- @treturn table the passed-in array
666 | -- @see push
667 | function _.addTop(array, ...)
668 | _.each({...},function(i,v) t_insert(array,1,v) end)
669 | return array
670 | end
671 |
672 | --- Pushes all passed-in values at the end of an array.
673 | -- @name push
674 | -- @tparam table array an array
675 | -- @tparam vararg ... a variable number of arguments
676 | -- @treturn table the passed-in array
677 | -- @see addTop
678 | function _.push(array, ...)
679 | _.each({...}, function(i,v) array[#array+1] = v end)
680 | return array
681 | end
682 |
683 | --- Removes and returns the values at the top of a given array.
684 | --
Aliased as `shift`
685 | -- @name pop
686 | -- @tparam table array an array
687 | -- @tparam[opt] number n the number of values to be popped. Defaults to 1.
688 | -- @treturn vararg a vararg list of values popped from the array
689 | -- @see unshift
690 | function _.pop(array, n)
691 | n = min(n or 1, #array)
692 | local ret = {}
693 | for i = 1, n do
694 | local retValue = array[1]
695 | ret[#ret + 1] = retValue
696 | t_remove(array,1)
697 | end
698 | return unpack(ret)
699 | end
700 |
701 | --- Removes and returns the values at the end of a given array.
702 | -- @name unshift
703 | -- @tparam table array an array
704 | -- @tparam[opt] number n the number of values to be unshifted. Defaults to 1.
705 | -- @treturn vararg a vararg list of values
706 | -- @see pop
707 | function _.unshift(array, n)
708 | n = min(n or 1, #array)
709 | local ret = {}
710 | for i = 1, n do
711 | local retValue = array[#array]
712 | ret[#ret + 1] = retValue
713 | t_remove(array)
714 | end
715 | return unpack(ret)
716 | end
717 |
718 | --- Removes all provided values in a given array.
719 | --
Aliased as `remove`
720 | -- @name pull
721 | -- @tparam table array an array
722 | -- @tparam vararg ... a variable number of values to be removed from the array
723 | -- @treturn table the passed-in array
724 | function _.pull(array, ...)
725 | for __, rmValue in ipairs({...}) do
726 | for i = #array, 1, -1 do
727 | if _.isEqual(array[i], rmValue) then
728 | t_remove(array, i)
729 | end
730 | end
731 | end
732 | return array
733 | end
734 |
735 | --- Trims all values indexed within the range `[start, finish]`.
736 | --
Aliased as `rmRange`
737 | -- @name removeRange
738 | -- @tparam table array an array
739 | -- @tparam[opt] number start the lower bound index, defaults to the first index in the array.
740 | -- @tparam[optchain] number finish the upper bound index, defaults to the array length.
741 | -- @treturn table the passed-in array
742 | function _.removeRange(array, start, finish)
743 | local array = _.clone(array)
744 | local i,n = (next(array)),#array
745 | if n < 1 then return array end
746 |
747 | start = clamp(start or i,i,n)
748 | finish = clamp(finish or n,i,n)
749 |
750 | if finish < start then return array end
751 |
752 | local count = finish - start + 1
753 | local i = start
754 | while count > 0 do
755 | t_remove(array,i)
756 | count = count - 1
757 | end
758 | return array
759 | end
760 |
761 | --- Chunks together consecutive values. Values are chunked on the basis of the return
762 | -- value of a provided predicate `f(key, value, ...)`. Consecutive elements which return
763 | -- the same value are chunked together. Leaves the first argument untouched if it is not an array.
764 | -- @name chunk
765 | -- @tparam table array an array
766 | -- @tparam function f an iterator function prototyped as `f(key, value, ...)`
767 | -- @tparam[opt] vararg ... Optional extra-args to be passed to function `f`
768 | -- @treturn table a table of chunks (arrays)
769 | -- @see zip
770 | function _.chunk(array, f, ...)
771 | if not _.isArray(array) then return array end
772 | local ch, ck, prev = {}, 0
773 | local mask = _.map(array, f,...)
774 | _.each(mask, function(k,v)
775 | prev = (prev==nil) and v or prev
776 | ck = ((v~=prev) and (ck+1) or ck)
777 | if not ch[ck] then
778 | ch[ck] = {array[k]}
779 | else
780 | ch[ck][#ch[ck]+1] = array[k]
781 | end
782 | prev = v
783 | end)
784 | return ch
785 | end
786 |
787 | --- Slices values indexed within `[start, finish]` range.
788 | --
Aliased as `_.sub`
789 | -- @name slice
790 | -- @tparam table array an array
791 | -- @tparam[opt] number start the lower bound index, defaults to the first index in the array.
792 | -- @tparam[optchain] number finish the upper bound index, defaults to the array length.
793 | -- @treturn table a new array
794 | function _.slice(array, start, finish)
795 | return _.select(array, function(index)
796 | return (index >= (start or next(array)) and index <= (finish or #array))
797 | end)
798 | end
799 |
800 | --- Returns the first N values in an array.
801 | --
Aliased as `head`, `take`
802 | -- @name first
803 | -- @tparam table array an array
804 | -- @tparam[opt] number n the number of values to be collected, defaults to 1.
805 | -- @treturn table a new array
806 | -- @see initial
807 | -- @see last
808 | -- @see rest
809 | function _.first(array, n)
810 | local n = n or 1
811 | return _.slice(array,1, min(n,#array))
812 | end
813 |
814 | --- Returns all values in an array excluding the last N values.
815 | -- @name initial
816 | -- @tparam table array an array
817 | -- @tparam[opt] number n the number of values to be left, defaults to the array length.
818 | -- @treturn table a new array
819 | -- @see first
820 | -- @see last
821 | -- @see rest
822 | function _.initial(array, n)
823 | if n and n < 0 then return end
824 | return _.slice(array,1, n and #array-(min(n,#array)) or #array-1)
825 | end
826 |
827 | --- Returns the last N values in an array.
828 | -- @name last
829 | -- @tparam table array an array
830 | -- @tparam[opt] number n the number of values to be collected, defaults to the array length.
831 | -- @treturn table a new array
832 | -- @see first
833 | -- @see initial
834 | -- @see rest
835 | function _.last(array,n)
836 | if n and n <= 0 then return end
837 | return _.slice(array,n and #array-min(n-1,#array-1) or 2,#array)
838 | end
839 |
840 | --- Trims all values before index.
841 | --
Aliased as `tail`
842 | -- @name rest
843 | -- @tparam table array an array
844 | -- @tparam[opt] number index an index, defaults to 1
845 | -- @treturn table a new array
846 | -- @see first
847 | -- @see initial
848 | -- @see last
849 | function _.rest(array,index)
850 | if index and index > #array then return {} end
851 | return _.slice(array,index and max(1,min(index,#array)) or 1,#array)
852 | end
853 |
854 | --- Trims all falsy (false and nil) values.
855 | -- @name compact
856 | -- @tparam table array an array
857 | -- @treturn table a new array
858 | function _.compact(array)
859 | return _.reject(array, function (_,value)
860 | return not value
861 | end)
862 | end
863 |
864 | --- Flattens a nested array. Passing `shallow` will only flatten at the first level.
865 | -- @name flatten
866 | -- @tparam table array an array
867 | -- @tparam[opt] boolean shallow specifies the flattening depth
868 | -- @treturn table a new array, flattened
869 | function _.flatten(array, shallow)
870 | local shallow = shallow or false
871 | local new_flattened
872 | local _flat = {}
873 | for key,value in pairs(array) do
874 | if _.isTable(value) then
875 | new_flattened = shallow and value or _.flatten (value)
876 | _.each(new_flattened, function(_,item) _flat[#_flat+1] = item end)
877 | else _flat[#_flat+1] = value
878 | end
879 | end
880 | return _flat
881 | end
882 |
883 | --- Returns values from an array not present in all passed-in args.
884 | --
Aliased as `without` and `diff`
885 | -- @name difference
886 | -- @tparam table array an array
887 | -- @tparam table another array
888 | -- @treturn table a new array
889 | -- @see union
890 | -- @see intersection
891 | -- @see symmetricDifference
892 | function _.difference(array, array2)
893 | if not array2 then return _.clone(array) end
894 | return _.select(array,function(i,value)
895 | return not _.include(array2,value)
896 | end)
897 | end
898 |
899 | --- Returns the duplicate-free union of all passed in arrays.
900 | -- @name union
901 | -- @tparam vararg ... a variable number of arrays arguments
902 | -- @treturn table a new array
903 | -- @see difference
904 | -- @see intersection
905 | -- @see symmetricDifference
906 | function _.union(...)
907 | return _.uniq(_.flatten({...}))
908 | end
909 |
910 | --- Returns the intersection of all passed-in arrays.
911 | -- Each value in the result is present in each of the passed-in arrays.
912 | -- @name intersection
913 | -- @tparam table array an array
914 | -- @tparam vararg ... a variable number of array arguments
915 | -- @treturn table a new array
916 | -- @see difference
917 | -- @see union
918 | -- @see symmetricDifference
919 | function _.intersection(array, ...)
920 | local arg = {...}
921 | local _intersect = {}
922 | for i,value in ipairs(array) do
923 | if _.all(arg,function(i,v)
924 | return _.include(v,value)
925 | end) then
926 | t_insert(_intersect,value)
927 | end
928 | end
929 | return _intersect
930 | end
931 |
932 | --- Performs a symmetric difference. Returns values from `array` not present in `array2` and also values
933 | -- from `array2` not present in `array`.
934 | --
Aliased as `symdiff`
935 | -- @name symmetricDifference
936 | -- @tparam table array an array
937 | -- @tparam table array2 another array
938 | -- @treturn table a new array
939 | -- @see difference
940 | -- @see union
941 | -- @see intersection
942 | function _.symmetricDifference(array, array2)
943 | return _.difference(
944 | _.union(array, array2),
945 | _.intersection(array,array2)
946 | )
947 | end
948 |
949 | --- Produces a duplicate-free version of a given array.
950 | --
Aliased as `uniq`
951 | -- @name unique
952 | -- @tparam table array an array
953 | -- @treturn table a new array, duplicate-free
954 | -- @see isunique
955 | function _.unique(array)
956 | local ret = {}
957 | for i = 1, #array do
958 | if not _.find(ret, array[i]) then
959 | ret[#ret+1] = array[i]
960 | end
961 | end
962 | return ret
963 | end
964 |
965 | --- Checks if a given array contains distinct values. Such an array is made of distinct elements,
966 | -- which only occur once in this array.
967 | --
Aliased as `isuniq`
968 | -- @name isunique
969 | -- @tparam table array an array
970 | -- @treturn boolean `true` if the given array is unique, `false` otherwise.
971 | -- @see unique
972 | function _.isunique(array)
973 | return _.isEqual(array, _.unique(array))
974 | end
975 |
976 | --- Merges values of each of the passed-in arrays in subsets.
977 | -- Only values indexed with the same key in the given arrays are merged in the same subset.
978 | -- @name zip
979 | -- @tparam vararg ... a variable number of array arguments
980 | -- @treturn table a new array
981 | function _.zip(...)
982 | local arg = {...}
983 | local _len = _.max(_.map(arg,function(i,v)
984 | return #v
985 | end))
986 | local _ans = {}
987 | for i = 1,_len do
988 | _ans[i] = _.pluck(arg,i)
989 | end
990 | return _ans
991 | end
992 |
993 | --- Clones `array` and appends `other` values.
994 | -- @name append
995 | -- @tparam table array an array
996 | -- @tparam table other an array
997 | -- @treturn table a new array
998 | function _.append(array, other)
999 | local t = {}
1000 | for i,v in ipairs(array) do t[i] = v end
1001 | for i,v in ipairs(other) do t[#t+1] = v end
1002 | return t
1003 | end
1004 |
1005 | --- Interleaves arrays. It returns a single array made of values from all
1006 | -- passed in arrays in their given order, interleaved.
1007 | -- @name interleave
1008 | -- @tparam vararg ... a variable list of arrays
1009 | -- @treturn table a new array
1010 | -- @see interpose
1011 | function _.interleave(...) return _.flatten(_.zip(...)) end
1012 |
1013 | --- Interposes `value` in-between consecutive pair of values in `array`.
1014 | -- @name interpose
1015 | -- @tparam value value a value
1016 | -- @tparam table array an array
1017 | -- @treturn table a new array
1018 | -- @see interleave
1019 | function _.interpose(value, array)
1020 | return _.flatten(_.zip(array, _.rep(value, #array-1)))
1021 | end
1022 |
1023 | --- Produce a flexible list of numbers. If one positive value is passed, will count from 0 to that value,
1024 | -- with a default step of 1. If two values are passed, will count from the first one to the second one, with the
1025 | -- same default step of 1. A third passed value will be considered a step value.
1026 | -- @name range
1027 | -- @tparam[opt] number from the initial value of the range
1028 | -- @tparam[optchain] number to the final value of the range
1029 | -- @tparam[optchain] number step the count step value
1030 | -- @treturn table a new array of numbers
1031 | function _.range(...)
1032 | local arg = {...}
1033 | local _start,_stop,_step
1034 | if #arg==0 then return {}
1035 | elseif #arg==1 then _stop,_start,_step = arg[1],0,1
1036 | elseif #arg==2 then _start,_stop,_step = arg[1],arg[2],1
1037 | elseif #arg == 3 then _start,_stop,_step = arg[1],arg[2],arg[3]
1038 | end
1039 | if (_step and _step==0) then return {} end
1040 | local _ranged = {}
1041 | local _steps = max(floor((_stop-_start)/_step),0)
1042 | for i=1,_steps do _ranged[#_ranged+1] = _start+_step*i end
1043 | if #_ranged>0 then t_insert(_ranged,1,_start) end
1044 | return _ranged
1045 | end
1046 |
1047 | --- Creates an array list of `n` values, repeated.
1048 | -- @name rep
1049 | -- @tparam value value a value to be repeated
1050 | -- @tparam number n the number of repetitions of the given `value`.
1051 | -- @treturn table a new array of `n` values
1052 | function _.rep(value, n)
1053 | local ret = {}
1054 | for i = 1, n do ret[#ret+1] = value end
1055 | return ret
1056 | end
1057 |
1058 | --- Iterator returning partitions of an array. It returns arrays of length `n`
1059 | -- made of values from the given array. In case the array size is not a multiple
1060 | -- of `n`, the last array returned will be made of the rest of the values.
1061 | -- @name partition.
1062 | -- @tparam table array an array
1063 | -- @tparam[opt] number n the size of each partition. Defaults to 1.
1064 | -- @treturn function an iterator function
1065 | function _.partition(array, n)
1066 | return coroutine.wrap(function()
1067 | partgen(array, n or 1, coroutine.yield)
1068 | end)
1069 | end
1070 |
1071 | --- Iterator returning the permutations of an array. It returns arrays made of all values
1072 | -- from the passed-in array, with values permuted.
1073 | -- @name permutation
1074 | -- @tparam table array an array
1075 | -- @treturn function an iterator function
1076 | function _.permutation(array)
1077 | return coroutine.wrap(function()
1078 | permgen(array, #array, coroutine.yield)
1079 | end)
1080 | end
1081 |
1082 | --- Swaps keys with values. Produces a new array where previous keys are now values,
1083 | -- while previous values are now keys.
1084 | --
Aliased as `mirror`
1085 | -- @name invert
1086 | -- @tparam table array a given array
1087 | -- @treturn table a new array
1088 | function _.invert(array)
1089 | local _ret = {}
1090 | _.each(array,function(i,v) _ret[v] = i end)
1091 | return _ret
1092 | end
1093 |
1094 | --- Concatenates values in a given array. Handles booleans as well. If `sep` string is
1095 | -- passed, it will be used as a separator. Passing `i` and `j` will result in concatenating
1096 | -- values within `[i,j]` range.
1097 | --
Aliased as `join`
1098 | -- @name concat
1099 | -- @tparam table array a given array
1100 | -- @tparam[opt] string sep a separator string, defaults to `''`.
1101 | -- @tparam[optchain] number i the starting index, defaults to 1.
1102 | -- @tparam[optchain] number j the final index, defaults to the array length.
1103 | -- @treturn string a string
1104 | function _.concat(array, sep, i, j)
1105 | local _array = _.map(array,function(i,v)
1106 | return tostring(v)
1107 | end)
1108 | return t_concat(_array,sep,i or 1,j or #array)
1109 |
1110 | end
1111 |
1112 |
1113 | --- Utility functions
1114 | -- @section Utility functions
1115 |
1116 | --- Returns the passed-in value. This function seems useless, but it is used internally
1117 | -- as a default iterator.
1118 | -- @name identity
1119 | -- @tparam value value a value
1120 | -- @treturn value the passed-in value
1121 | function _.identity(value) return value end
1122 |
1123 | --- Returns a version of `f` that runs only once. Successive calls to `f`
1124 | -- will keep yielding the same output, no matter what the passed-in arguments are.
1125 | -- It can be used to initialize variables.
1126 | -- @name once
1127 | -- @tparam function f a function
1128 | -- @treturn function a new function
1129 | -- @see after
1130 | function _.once(f)
1131 | local _internal = 0
1132 | local _args = {}
1133 | return function(...)
1134 | _internal = _internal+1
1135 | if _internal<=1 then _args = {...} end
1136 | return f(unpack(_args))
1137 | end
1138 | end
1139 |
1140 | --- Memoizes a given function by caching the computed result.
1141 | -- Useful for speeding-up slow-running functions. If function `hash` is passed,
1142 | -- it will be used to compute hash keys for a set of input values to the function for caching.
1143 | --
Aliased as `cache`
1144 | -- @name memoize
1145 | -- @tparam function f a function
1146 | -- @tparam[opt] function hash a hash function, defaults to @{identity}
1147 | -- @treturn function a new function
1148 | function _.memoize(f, hash)
1149 | local _cache = setmetatable({},{__mode = 'kv'})
1150 | local _hasher = hash or _.identity
1151 | return function (...)
1152 | local _hashKey = _hasher(...)
1153 | local _result = _cache[_hashKey]
1154 | if not _result then _cache[_hashKey] = f(...) end
1155 | return _cache[_hashKey]
1156 | end
1157 | end
1158 |
1159 | --- Returns a version of `f` that runs on the `count-th` call.
1160 | -- Useful when dealing with asynchronous tasks.
1161 | -- @name after
1162 | -- @tparam function f a function
1163 | -- @tparam number count the number of calls before `f` answers
1164 | -- @treturn function a new function
1165 | -- @see once
1166 | function _.after(f, count)
1167 | local _limit,_internal = count, 0
1168 | return function(...)
1169 | _internal = _internal+1
1170 | if _internal >= _limit then return f(...) end
1171 | end
1172 | end
1173 |
1174 | --- Composes functions. Each passed-in function consumes the return value of the function that follows.
1175 | -- In math terms, composing the functions `f`, `g`, and `h` produces the function `f(g(h(...)))`.
1176 | -- @name compose
1177 | -- @tparam vararg ... a variable number of functions
1178 | -- @treturn function a new function
1179 | -- @see pipe
1180 | function _.compose(...)
1181 | local f = _.reverse {...}
1182 | return function (...)
1183 | local _temp
1184 | for i, func in ipairs(f) do
1185 | _temp = _temp and func(_temp) or func(...)
1186 | end
1187 | return _temp
1188 | end
1189 | end
1190 |
1191 | --- Pipes a value through a series of functions. In math terms,
1192 | -- given some functions `f`, `g`, and `h` in that order, it returns `f(g(h(value)))`.
1193 | -- @name pipe
1194 | -- @tparam value value a value
1195 | -- @tparam vararg ... a variable number of functions
1196 | -- @treturn value the result of the composition of function calls.
1197 | -- @see compose
1198 | function _.pipe(value, ...)
1199 | return _.compose(...)(value)
1200 | end
1201 |
1202 | --- Returns the logical complement of a given function. For a given input, the returned
1203 | -- function will output `false` if the original function would have returned `true`,
1204 | -- and vice-versa.
1205 | -- @name complement
1206 | -- @tparam function f a function
1207 | -- @treturn function the logical complement of the given function `f`.
1208 | function _.complement(f)
1209 | return function(...) return not f(...) end
1210 | end
1211 |
1212 | --- Calls a sequence of passed-in functions with the same argument.
1213 | -- Returns a sequence of results.
1214 | --
Aliased as `juxt`
1215 | -- @name juxtapose
1216 | -- @tparam value value a value
1217 | -- @tparam vararg ... a variable number of functions
1218 | -- @treturn vararg a vargarg list of results.
1219 | function _.juxtapose(value, ...)
1220 | local res = {}
1221 | _.each({...}, function(_,f) res[#res+1] = f(value) end)
1222 | return unpack(res)
1223 | end
1224 |
1225 | --- Wraps `f` inside of the `wrapper` function. It passes `f` as the first argument to `wrapper`.
1226 | -- This allows the wrapper to execute code before and after `f` runs,
1227 | -- adjust the arguments, and execute it conditionally.
1228 | -- @name wrap
1229 | -- @tparam function f a function to be wrapped, prototyped as `f(...)`
1230 | -- @tparam function wrapper a wrapper function, prototyped as `wrapper(f,...)`
1231 | -- @treturn function a new function
1232 | function _.wrap(f, wrapper)
1233 | return function (...) return wrapper(f,...) end
1234 | end
1235 |
1236 | --- Runs `iter` function `n` times.
1237 | -- Collects the results of each run and returns them in an array.
1238 | -- @name times
1239 | -- @tparam number n the number of times `iter` should be called
1240 | -- @tparam function iter an iterator function, prototyped as `iter(i, ...)`
1241 | -- @tparam vararg ... extra-args to be passed to `iter` function
1242 | -- @treturn table an array of results
1243 | function _.times(n, iter, ...)
1244 | local results = {}
1245 | for i = 1,n do
1246 | results[i] = iter(i,...)
1247 | end
1248 | return results
1249 | end
1250 |
1251 | --- Binds `v` to be the first argument to function `f`. As a result,
1252 | -- calling `f(...)` will result to `f(v, ...)`.
1253 | -- @name bind
1254 | -- @tparam function f a function
1255 | -- @tparam value v a value
1256 | -- @treturn function a function
1257 | -- @see bindn
1258 | function _.bind(f, v)
1259 | return function (...)
1260 | return f(v,...)
1261 | end
1262 | end
1263 |
1264 | --- Binds `...` to be the N-first arguments to function `f`. As a result,
1265 | -- calling `f(a1, a2, ..., aN)` will result to `f(..., a1, a2, ...,aN)`.
1266 | -- @name bindn
1267 | -- @tparam function f a function
1268 | -- @tparam vararg ... a variable number of arguments
1269 | -- @treturn function a function
1270 | -- @see bind
1271 | function _.bindn(f, ...)
1272 | local iArg = {...}
1273 | return function (...)
1274 | return f(unpack(_.append(iArg,{...})))
1275 | end
1276 | end
1277 |
1278 | --- Generates a unique ID for the current session. If given a string *template*
1279 | -- will use this template for output formatting. Otherwise, if *template* is a function,
1280 | -- will evaluate `template(id, ...)`.
1281 | --
Aliased as `uid`.
1282 | -- @name uniqueId
1283 | -- @tparam[opt] string|function template either a string or a function template to format the ID
1284 | -- @tparam[optchain] vararg ... a variable number of arguments to be passed to *template*, in case it is a function.
1285 | -- @treturn value an ID
1286 | function _.uniqueId(template, ...)
1287 | unique_id_counter = unique_id_counter + 1
1288 | if template then
1289 | if _.isString(template) then
1290 | return template:format(unique_id_counter)
1291 | elseif _.isFunction(template) then
1292 | return template(unique_id_counter,...)
1293 | end
1294 | end
1295 | return unique_id_counter
1296 | end
1297 |
1298 | --- Object functions
1299 | --@section Object functions
1300 |
1301 | --- Returns the keys of the object properties.
1302 | -- @name keys
1303 | -- @tparam table obj an object
1304 | -- @treturn table an array
1305 | function _.keys(obj)
1306 | local _oKeys = {}
1307 | _.each(obj,function(key) _oKeys[#_oKeys+1]=key end)
1308 | return _oKeys
1309 | end
1310 |
1311 | --- Returns the values of the object properties.
1312 | -- @name values
1313 | -- @tparam table obj an object
1314 | -- @treturn table an array
1315 | function _.values(obj)
1316 | local _oValues = {}
1317 | _.each(obj,function(_,value) _oValues[#_oValues+1]=value end)
1318 | return _oValues
1319 | end
1320 |
1321 | --- Converts any given value to a boolean
1322 | -- @name toBoolean
1323 | -- @tparam value value a value. Can be of any type
1324 | -- @treturn boolean `true` if value is true, `false` otherwise (false or nil).
1325 | function _.toBoolean(value)
1326 | return not not value
1327 | end
1328 |
1329 | --- Extends an object properties. It copies all of the properties of extra passed-in objects
1330 | -- into the destination object, and returns the destination object.
1331 | -- The last object in the `...` set will override properties of the same name in the previous one
1332 | -- @name extend
1333 | -- @tparam table destObj a destination object
1334 | -- @tparam vararg ... a variable number of array arguments
1335 | -- @treturn table the destination object extended
1336 | function _.extend(destObj, ...)
1337 | local sources = {...}
1338 | _.each(sources,function(__,source)
1339 | if _.isTable(source) then
1340 | _.each(source,function(key,value)
1341 | destObj[key] = value
1342 | end)
1343 | end
1344 | end)
1345 | return destObj
1346 | end
1347 |
1348 | --- Returns a sorted list of all methods names found in an object. If the given object
1349 | -- has a metatable implementing an `__index` field pointing to another table, will also recurse on this
1350 | -- table if argument `recurseMt` is provided. If `obj` is omitted, it defaults to the library functions.
1351 | --
Aliased as `methods`.
1352 | -- @name functions
1353 | -- @tparam[opt] table obj an object. Defaults to library functions.
1354 | -- @treturn table an array-list of methods names
1355 | function _.functions(obj, recurseMt)
1356 | obj = obj or _
1357 | local _methods = {}
1358 | _.each(obj,function(key,value)
1359 | if _.isFunction(value) then
1360 | _methods[#_methods+1]=key
1361 | end
1362 | end)
1363 | if not recurseMt then
1364 | return _.sort(_methods)
1365 | end
1366 | local mt = getmetatable(obj)
1367 | if mt and mt.__index then
1368 | local mt_methods = _.functions(mt.__index)
1369 | _.each(mt_methods, function(k,fn)
1370 | _methods[#_methods+1] = fn
1371 | end)
1372 | end
1373 | return _.sort(_methods)
1374 | end
1375 |
1376 | --- Clones a given object properties. If `shallow` is passed
1377 | -- will also clone nested array properties.
1378 | -- @name clone
1379 | -- @tparam table obj an object
1380 | -- @tparam[opt] boolean shallow whether or not nested array-properties should be cloned, defaults to false.
1381 | -- @treturn table a copy of the passed-in object
1382 | function _.clone(obj, shallow)
1383 | if not _.isTable(obj) then return obj end
1384 | local _obj = {}
1385 | _.each(obj,function(i,v)
1386 | if _.isTable(v) then
1387 | if not shallow then
1388 | _obj[i] = _.clone(v,shallow)
1389 | else _obj[i] = v
1390 | end
1391 | else
1392 | _obj[i] = v
1393 | end
1394 | end)
1395 | return _obj
1396 | end
1397 |
1398 | --- Invokes interceptor with the object, and then returns object.
1399 | -- The primary purpose of this method is to "tap into" a method chain, in order to perform operations
1400 | -- on intermediate results within the chain.
1401 | -- @name tap
1402 | -- @tparam table obj an object
1403 | -- @tparam function f an interceptor function, should be prototyped as `f(obj, ...)`
1404 | -- @tparam[opt] vararg ... Extra-args to be passed to interceptor function
1405 | -- @treturn table the passed-in object
1406 | function _.tap(obj, f, ...)
1407 | f(obj,...)
1408 | return obj
1409 | end
1410 |
1411 | --- Checks if a given object implements a property.
1412 | -- @name has
1413 | -- @tparam table obj an object
1414 | -- @tparam value key a key property to be checked
1415 | -- @treturn boolean `true` or `false`
1416 | function _.has(obj, key)
1417 | return obj[key]~=nil
1418 | end
1419 |
1420 | --- Return a filtered copy of the object. The returned object will only have
1421 | -- the white-listed properties paired with their original values.
1422 | --
Aliased as `choose`.
1423 | -- @name pick
1424 | -- @tparam table obj an object
1425 | -- @tparam vararg ... a variable number of string keys
1426 | -- @treturn table the filtered object
1427 | function _.pick(obj, ...)
1428 | local whitelist = _.flatten {...}
1429 | local _picked = {}
1430 | _.each(whitelist,function(key,property)
1431 | if not _.isNil(obj[property]) then
1432 | _picked[property] = obj[property]
1433 | end
1434 | end)
1435 | return _picked
1436 | end
1437 |
1438 | --- Return a filtered copy of the object. The returned object will not have
1439 | -- the black-listed properties.
1440 | --
Aliased as `drop`.
1441 | -- @name omit
1442 | -- @tparam table obj an object
1443 | -- @tparam vararg ... a variable number of string keys
1444 | -- @treturn table the filtered object
1445 | function _.omit(obj, ...)
1446 | local blacklist = _.flatten {...}
1447 | local _picked = {}
1448 | _.each(obj,function(key,value)
1449 | if not _.include(blacklist,key) then
1450 | _picked[key] = value
1451 | end
1452 | end)
1453 | return _picked
1454 | end
1455 |
1456 | --- Fills nil properties in an object with the given `template` object. Pre-existing
1457 | -- properties will be preserved.
1458 | --
Aliased as `defaults`.
1459 | -- @name template
1460 | -- @tparam table obj an object
1461 | -- @tparam[opt] table template a template object. Defaults to an empty table `{}`.
1462 | -- @treturn table the passed-in object filled
1463 | function _.template(obj, template)
1464 | _.each(template or {},function(i,v)
1465 | if not obj[i] then obj[i] = v end
1466 | end)
1467 | return obj
1468 | end
1469 |
1470 | --- Performs a deep comparison test between two objects. Can compare strings, functions
1471 | -- (by reference), nil, booleans. Compares tables by reference or by values. If `useMt`
1472 | -- is passed, the equality operator `==` will be used if one of the given objects has a
1473 | -- metatable implementing `__eq`.
1474 | --
Aliased as `_.compare`
1475 | -- @name isEqual
1476 | -- @tparam table objA an object
1477 | -- @tparam table objB another object
1478 | -- @tparam[opt] boolean useMt whether or not `__eq` should be used, defaults to false.
1479 | -- @treturn boolean `true` or `false`
1480 | function _.isEqual(objA, objB, useMt)
1481 | local typeObjA = type(objA)
1482 | local typeObjB = type(objB)
1483 |
1484 | if typeObjA~=typeObjB then return false end
1485 | if typeObjA~='table' then return (objA==objB) end
1486 |
1487 | local mtA = getmetatable(objA)
1488 | local mtB = getmetatable(objB)
1489 |
1490 | if useMt then
1491 | if (mtA or mtB) and (mtA.__eq or mtB.__eq) then
1492 | return mtA.__eq(objA, objB) or mtB.__eq(objB, objA) or (objA==objB)
1493 | end
1494 | end
1495 |
1496 | if _.size(objA)~=_.size(objB) then return false end
1497 |
1498 | for i,v1 in pairs(objA) do
1499 | local v2 = objB[i]
1500 | if _.isNil(v2) or not _.isEqual(v1,v2,useMt) then return false end
1501 | end
1502 |
1503 | for i,v1 in pairs(objB) do
1504 | local v2 = objA[i]
1505 | if _.isNil(v2) then return false end
1506 | end
1507 |
1508 | return true
1509 | end
1510 |
1511 | --- Invokes an object method. It passes the object itself as the first argument. if `method` is not
1512 | -- callable, will return `obj[method]`.
1513 | -- @name result
1514 | -- @tparam table obj an object
1515 | -- @tparam string method a string key to index in object `obj`.
1516 | -- @tparam[opt] vararg ... Optional extra-args to be passed to `method`
1517 | -- @treturn value the returned value of `method(obj,...)` call
1518 | function _.result(obj, method, ...)
1519 | if obj[method] then
1520 | if _.isCallable(obj[method]) then
1521 | return obj[method](obj,...)
1522 | else return obj[method]
1523 | end
1524 | end
1525 | if _.isCallable(method) then
1526 | return method(obj,...)
1527 | end
1528 | end
1529 |
1530 | --- Checks if the given arg is a table.
1531 | -- @name isTable
1532 | -- @tparam table t a value to be tested
1533 | -- @treturn boolean `true` or `false`
1534 | function _.isTable(t)
1535 | return type(t) == 'table'
1536 | end
1537 |
1538 | --- Checks if the given argument is an callable. Assumes `obj` is callable if
1539 | -- it is either a function or a table having a metatable implementing `__call` metamethod.
1540 | -- @name isCallable
1541 | -- @tparam table obj an object
1542 | -- @treturn boolean `true` or `false`
1543 | function _.isCallable(obj)
1544 | return (_.isFunction(obj) or
1545 | (_.isTable(obj) and getmetatable(obj)
1546 | and getmetatable(obj).__call~=nil) or false)
1547 | end
1548 |
1549 | --- Checks if the given argument is an array. Assumes `obj` is an array
1550 | -- if is a table with integer numbers starting at 1.
1551 | -- @name isArray
1552 | -- @tparam table obj an object
1553 | -- @treturn boolean `true` or `false`
1554 | function _.isArray(obj)
1555 | if not _.isTable(obj) then return false end
1556 | -- Thanks @Wojak and @Enrique García Cota for suggesting this
1557 | -- See : http://love2d.org/forums/viewtopic.php?f=3&t=77255&start=40#p163624
1558 | local i = 0
1559 | for __ in pairs(obj) do
1560 | i = i + 1
1561 | if _.isNil(obj[i]) then return false end
1562 | end
1563 | return true
1564 | end
1565 |
1566 | --- Checks if the given object is iterable with `pairs` (or `ipairs`).
1567 | -- @name isIterable
1568 | -- @tparam table obj an object
1569 | -- @treturn boolean `true` if the object can be iterated with `pairs`, `false` otherwise
1570 | function _.isIterable(obj)
1571 | return _.toBoolean((pcall(pairs, obj)))
1572 | end
1573 |
1574 | --- Checks if the given is empty. If `obj` is a *string*, will return `true`
1575 | -- if `#obj == 0`. Otherwise, if `obj` is a table, will return whether or not this table
1576 | -- is empty. If `obj` is `nil`, it will return true.
1577 | -- @name isEmpty
1578 | -- @tparam[opt] table|string obj an object
1579 | -- @treturn boolean `true` or `false`
1580 | function _.isEmpty(obj)
1581 | if _.isNil(obj) then return true end
1582 | if _.isString(obj) then return #obj==0 end
1583 | if _.isTable(obj) then return next(obj)==nil end
1584 | return true
1585 | end
1586 |
1587 | --- Checks if the given argument is a *string*.
1588 | -- @name isString
1589 | -- @tparam table obj an object
1590 | -- @treturn boolean `true` or `false`
1591 | function _.isString(obj)
1592 | return type(obj) == 'string'
1593 | end
1594 |
1595 | --- Checks if the given argument is a function.
1596 | -- @name isFunction
1597 | -- @tparam table obj an object
1598 | -- @treturn boolean `true` or `false`
1599 | function _.isFunction(obj)
1600 | return type(obj) == 'function'
1601 | end
1602 |
1603 | --- Checks if the given argument is nil.
1604 | -- @name isNil
1605 | -- @tparam table obj an object
1606 | -- @treturn boolean `true` or `false`
1607 | function _.isNil(obj)
1608 | return obj==nil
1609 | end
1610 |
1611 | --- Checks if the given argument is a number.
1612 | -- @name isNumber
1613 | -- @tparam table obj a number
1614 | -- @treturn boolean `true` or `false`
1615 | -- @see isNaN
1616 | function _.isNumber(obj)
1617 | return type(obj) == 'number'
1618 | end
1619 |
1620 | --- Checks if the given argument is NaN (see [Not-A-Number](http://en.wikipedia.org/wiki/NaN)).
1621 | -- @name isNaN
1622 | -- @tparam table obj a number
1623 | -- @treturn boolean `true` or `false`
1624 | -- @see isNumber
1625 | function _.isNaN(obj)
1626 | return _.isNumber(obj) and obj~=obj
1627 | end
1628 |
1629 | --- Checks if the given argument is a finite number.
1630 | -- @name isFinite
1631 | -- @tparam table obj a number
1632 | -- @treturn boolean `true` or `false`
1633 | function _.isFinite(obj)
1634 | if not _.isNumber(obj) then return false end
1635 | return obj > -huge and obj < huge
1636 | end
1637 |
1638 | --- Checks if the given argument is a boolean.
1639 | -- @name isBoolean
1640 | -- @tparam table obj a boolean
1641 | -- @treturn boolean `true` or `false`
1642 | function _.isBoolean(obj)
1643 | return type(obj) == 'boolean'
1644 | end
1645 |
1646 | --- Checks if the given argument is an integer.
1647 | -- @name isInteger
1648 | -- @tparam table obj a number
1649 | -- @treturn boolean `true` or `false`
1650 | function _.isInteger(obj)
1651 | return _.isNumber(obj) and floor(obj)==obj
1652 | end
1653 |
1654 | -- Aliases
1655 |
1656 | do
1657 |
1658 | -- Table functions aliases
1659 | _.forEach = _.each
1660 | _.forEachi = _.eachi
1661 | _.loop = _.cycle
1662 | _.collect = _.map
1663 | _.inject = _.reduce
1664 | _.foldl = _.reduce
1665 | _.injectr = _.reduceRight
1666 | _.foldr = _.reduceRight
1667 | _.mapr = _.mapReduce
1668 | _.maprr = _.mapReduceRight
1669 | _.any = _.include
1670 | _.some = _.include
1671 | _.filter = _.select
1672 | _.discard = _.reject
1673 | _.every = _.all
1674 |
1675 | -- Array functions aliases
1676 | _.takeWhile = _.selectWhile
1677 | _.rejectWhile = _.dropWhile
1678 | _.shift = _.pop
1679 | _.remove = _.pull
1680 | _.rmRange = _.removeRange
1681 | _.chop = _.removeRange
1682 | _.sub = _.slice
1683 | _.head = _.first
1684 | _.take = _.first
1685 | _.tail = _.rest
1686 | _.skip = _.last
1687 | _.without = _.difference
1688 | _.diff = _.difference
1689 | _.symdiff = _.symmetricDifference
1690 | _.xor = _.symmetricDifference
1691 | _.uniq = _.unique
1692 | _.isuniq = _.isunique
1693 | _.part = _.partition
1694 | _.perm = _.permutation
1695 | _.mirror = _.invert
1696 | _.join = _.concat
1697 |
1698 | -- Utility functions aliases
1699 | _.cache = _.memoize
1700 | _.juxt = _.juxtapose
1701 | _.uid = _.uniqueId
1702 |
1703 | -- Object functions aliases
1704 | _.methods = _.functions
1705 | _.choose = _.pick
1706 | _.drop = _.omit
1707 | _.defaults = _.template
1708 | _.compare = _.isEqual
1709 |
1710 | end
1711 |
1712 | -- Setting chaining and building interface
1713 |
1714 | do
1715 |
1716 | -- Wrapper to Moses
1717 | local f = {}
1718 |
1719 | -- Will be returned upon requiring, indexes into the wrapper
1720 | local __ = {}
1721 | __.__index = f
1722 |
1723 | -- Wraps a value into an instance, and returns the wrapped object
1724 | local function new(value)
1725 | local i = {_value = value, _wrapped = true}
1726 | return setmetatable(i, __)
1727 | end
1728 |
1729 | setmetatable(__,{
1730 | __call = function(self,v) return new(v) end, -- Calls returns to instantiation
1731 | __index = function(t,key,...) return f[key] end -- Redirects to the wrapper
1732 | })
1733 |
1734 | --- Returns a wrapped object. Calling library functions as methods on this object
1735 | -- will continue to return wrapped objects until @{obj:value} is used. Can be aliased as `_(value)`.
1736 | -- @class function
1737 | -- @name chain
1738 | -- @tparam value value a value to be wrapped
1739 | -- @treturn object a wrapped object
1740 | function __.chain(value)
1741 | return new(value)
1742 | end
1743 |
1744 | --- Extracts the value of a wrapped object. Must be called on an chained object (see @{chain}).
1745 | -- @class function
1746 | -- @name obj:value
1747 | -- @treturn value the value previously wrapped
1748 | function __:value()
1749 | return self._value
1750 | end
1751 |
1752 | -- Register chaining methods into the wrapper
1753 | f.chain, f.value = __.chain, __.value
1754 |
1755 | -- Register all functions into the wrapper
1756 | for fname,fct in pairs(_) do
1757 | f[fname] = function(v, ...)
1758 | local wrapped = _.isTable(v) and v._wrapped or false
1759 | if wrapped then
1760 | local _arg = v._value
1761 | local _rslt = fct(_arg,...)
1762 | return new(_rslt)
1763 | else
1764 | return fct(v,...)
1765 | end
1766 | end
1767 | end
1768 |
1769 | --- Imports all library functions into a context.
1770 | -- @name import
1771 | -- @tparam[opt] table context a context. Defaults to `_G` (global environment) when not given.
1772 | -- @tparam[optchain] boolean noConflict Skips function import in case its key exists in the given context
1773 | -- @treturn table the passed-in context
1774 | f.import = function(context, noConflict)
1775 | context = context or _G
1776 | local funcs = _.functions()
1777 | _.each(funcs, function(k, fname)
1778 | if rawget(context, fname) then
1779 | if not noConflict then
1780 | context[fname] = _[fname]
1781 | end
1782 | else
1783 | context[fname] = _[fname]
1784 | end
1785 | end)
1786 | return context
1787 | end
1788 |
1789 | -- Descriptive tags
1790 | __._VERSION = 'Moses v'.._MODULEVERSION
1791 | __._URL = 'http://github.com/Yonaba/Moses'
1792 | __._LICENSE = 'MIT '
1793 | __._DESCRIPTION = 'utility-belt library for functional programming in Lua'
1794 |
1795 | return __
1796 |
1797 | end
1798 |
--------------------------------------------------------------------------------
/hua/core/oo.hy:
--------------------------------------------------------------------------------
1 | (import [hy.models.list [HyList]])
2 |
3 | (defmacro super [method &rest body]
4 | `((get --hua-parent-- ~(string method)) self ~@body))
5 |
6 | (defmacro defclass [class-name base-list body]
7 | (when (not (instance? HyList base-list))
8 | (macro-error base-list "defclass's second argument should be a list"))
9 | (def parent-name
10 | (if (empty? base-list)
11 | nil
12 | (first base-list)))
13 |
14 | (def class-name-string (string class-name))
15 |
16 | ;; (try
17 | ;; (def body-expression (iter body))
18 | ;; (catch [e TypeError]
19 | ;; (macro-error body
20 | ;; "Wrong argument type for defclass attributes definition.")))
21 |
22 | (def body-expression (iter body))
23 |
24 | (def arglist {})
25 |
26 | (for [b body-expression]
27 | (when (!= 2 (len b))
28 | (macro-error body-expression
29 | "Wrong number of argument in defclass attribute."))
30 | (assoc arglist (string (first b)) (second b)))
31 |
32 | (def class-index-expr
33 | (if parent-name
34 | '(fn [cls name]
35 | (def val (rawget --hua-base-- name))
36 | (if (= val nil)
37 | (get --hua-parent-- name)
38 | val))
39 | '--hua-base--))
40 |
41 | `(do
42 | (def ~class-name nil)
43 | (do-block
44 | (def --hua-parent-- ~parent-name)
45 | (def --hua-base-- ~arglist)
46 | (def --hua-class-string-- ~class-name-string)
47 | (setv --hua-base--.--index --hua-base--)
48 | (when --hua-parent--
49 | (setmetatable --hua-base-- --hua-parent--.--base))
50 | (defn --hua-cls-call-- [cls *dotdotdot*]
51 | (def --hua-self-- (setmetatable {} --hua-base--))
52 | (.--init --hua-self-- *dotdotdot*)
53 | --hua-self--)
54 | (def --hua-class--
55 | (setmetatable {"__base" --hua-base-- "__name" --hua-class-string-- "__parent" --hua-parent--}
56 |
57 | {"__index" ~class-index-expr "__call" --hua-cls-call--}))
58 | (setv --hua-base--.--class --hua-class--)
59 | (when (and --hua-parent--
60 | --hua-parent--.--inherited)
61 | (--hua-parent--.--inherited --hua-parent-- --hua-class--))
62 | (setv ~class-name --hua-class--))))
63 |
--------------------------------------------------------------------------------
/hua/core/op.hy:
--------------------------------------------------------------------------------
1 | (import [hua.core.utils [hua-gensym simple-form?]])
2 |
3 | ;;; compare operation with more than 2 arguments
4 |
5 | (defn my-eq [a b]
6 | (= a b))
7 |
8 | (defn my-lt [a b]
9 | (< a b))
10 |
11 | ;;; operators like =* (opstar) can only accept two arguments
12 | ;;; The macro below will generate macros like = (op), which will accept two or more arguments
13 | (defmacro --def-hua-comp-op-- [op opstar]
14 | `(defmacro ~op [&rest exprs]
15 | (def op* (quote ~opstar))
16 | (when (my-lt (len exprs) 2)
17 | (macro-error exprs
18 | "comparison operator needs at least 2 operands"))
19 |
20 | (if (my-eq (len exprs) 2)
21 | ;; if only two arguments are given, return the normal form of opstar
22 | `(~op* ~(get exprs 0)
23 | ~(get exprs 1))
24 |
25 | ;; if more than two arguments are given, we will expand to the form of (and (opstar e1 e2) (opstar e2 e3) ...)
26 | ;; The problems is that expressions like e2 will be evaluated twice. We need temporary variables to hold the evaluated value of e2.
27 | (let [[temp-vars (list-comp (if (simple-form? expr)
28 | nil
29 | (hua-gensym))
30 | [expr exprs])]
31 | [binding-body (list-comp `(def ~(get temp-vars i) ~(get exprs i))
32 | [i (range (len exprs))]
33 | (not (nil? (get temp-vars i))))]
34 | [compare-vars (list-comp (if (simple-form? expr)
35 | expr
36 | (hua-gensym))
37 | [expr exprs])]
38 | [comparing-body (list-comp `(~op* ~(get compare-vars (- i 1))
39 | ~(get compare-vars i))
40 | [i (range 1 (len compare-vars))])]]
41 | `(do
42 | ~@binding-body
43 | (and ~@comparing-body))))))
44 |
45 | (--def-hua-comp-op-- = =*)
46 | (--def-hua-comp-op-- < <*)
47 | (--def-hua-comp-op-- <= <=*)
48 |
49 | (defmacro >* [l r]
50 | `(not (<=* ~l ~r)))
51 |
52 | (defmacro >=* [l r]
53 | `(not (<* ~l ~r)))
54 |
55 | (defmacro !=* [l r]
56 | `(not (=* ~l ~r)))
57 |
58 | (--def-hua-comp-op-- > >*)
59 | (--def-hua-comp-op-- >= >=*)
60 | (--def-hua-comp-op-- != !=*)
61 |
--------------------------------------------------------------------------------
/hua/core/utils.hy:
--------------------------------------------------------------------------------
1 | (import [hy.models.symbol [HySymbol]])
2 |
3 | (defn hua-gensym [&optional [sym nil]]
4 | (def temp-sym (string (if sym
5 | (gensym sym)
6 | (gensym))))
7 | (HySymbol (.replace temp-sym ":" "_hua_")))
8 |
9 | ;;; if a form is a string, a number or a symbol
10 | (defn simple-form? [o]
11 | (or (string? o)
12 | (numeric? o)
13 | (instance? HySymbol o)))
14 |
--------------------------------------------------------------------------------
/hua/lua.hy:
--------------------------------------------------------------------------------
1 | (import [os.path [realpath dirname]])
2 | (import lupa)
3 | (import [lupa [LuaRuntime]])
4 |
5 | (def current-path (dirname(realpath --file--)))
6 |
7 | (defn init-lua []
8 | "return a lua runtime"
9 | (apply LuaRuntime [] {"unpack_returned_tuples true"}))
10 |
11 | (def lua (init-lua))
12 |
13 | (defn -dict? [o]
14 | "if o is an instance of dictionary"
15 | (instance? dict o))
16 |
17 | (defn -lt? [o]
18 | "if o is an instance of list or tuple"
19 | (instance? (, list tuple) o))
20 |
21 | (defn lt->dict [lt]
22 | "convert a list/tuple to lua table style dict (index start with 1)"
23 | (let [[r {}]]
24 | (for [(, i v) (enumerate lt)]
25 | (assoc r (inc i) v))
26 | r))
27 |
28 | (defn -dict->ltable [lua-runtime d-]
29 | "recursively convert a python dictionary into lua table"
30 | (let [[r {}]
31 | [d (if (-lt? d-)
32 | (lt->dict d-)
33 | d-)]]
34 | (for [(, k v) (.items d)]
35 | (let [[new-v (cond
36 | [(-dict? v) (-dict->ltable lua-runtime v)]
37 | [(-lt? v) (-dict->ltable lua-runtime (lt->dict v))]
38 | [true v])]]
39 | (assoc r k new-v)))
40 | (.table-from lua-runtime r)))
41 |
42 |
43 | (def tlua (init-lua))
44 | (let [[lua-package-table (.require tlua "package")]
45 | [lua-package-path (. lua-package-table path)]]
46 | (assoc lua-package-table
47 | "path"
48 | (+ lua-package-path
49 | ";"
50 | current-path
51 | "/?.lua")))
52 |
53 | (def tlcode (.require tlua "tlcode"))
54 |
55 | (defn tlast->src [ast-table]
56 | (tlcode.generate (.table-from tlua (-dict->ltable tlua ast-table))))
57 |
--------------------------------------------------------------------------------
/hua/mlast.hy:
--------------------------------------------------------------------------------
1 | (import [numbers [Real]])
2 |
3 | (import [hua.lua [lt->dict]])
4 |
5 | (def *op-ids* {:+ "add"
6 | :- "sub"
7 | :* "mul"
8 | :/ "div"
9 | :// "idiv"
10 | :% "mod"
11 | :^ "pow"
12 | :& "band"
13 | :bor "bor"
14 | :<< "shl"
15 | :>> "shr"
16 | :concat "concat"
17 | :=* "eq"
18 | :<* "lt"
19 | :<=* "le"
20 | :~=* "ne"
21 | :>* "gt"
22 | :>=* "ge"
23 | :and "and"
24 | :or "or"
25 | :not "not"
26 | :len "len"})
27 |
28 | ;;; given the operator name in hua, return corresponding op-id
29 | (defn get-op-id [op]
30 | (if (= op "|")
31 | "bor"
32 | (get *op-ids* (keyword op))))
33 |
34 | (defclass ASTNode [object]
35 | [[--init--
36 | (fn [self]
37 | (setv self.nodes [])
38 | nil)]
39 | [gen-repr-template
40 | (fn [self]
41 | (.join "" [""]))]
42 | [--repr--
43 | (fn [self]
44 | (% (.gen-repr-template self) (% "nodes: %s" self.nodes)))]
45 | [to-ml-table
46 | (fn [self]
47 | (let [[res {"tag" self.tag}]
48 | [-nodes (list-comp (cond [(instance? ASTNode node)
49 | (.to-ml-table node)]
50 | [(instance? dict node)
51 | (dict-comp key (.to-ml-table (get node key)) [key (.keys node)])]
52 | [(instance? (, list tuple) node)
53 | (lt->dict (list-comp (.to-ml-table subnode)
54 | [subnode node]))]
55 | [(or (instance? Real node)
56 | (string? node))
57 | node]
58 | [true
59 | (. node expr nodes)])
60 | [node self.nodes])]
61 | [nodes (lt->dict -nodes)]]
62 | (.update res nodes)
63 | (if (instance? Multi self)
64 | nodes
65 | res)))]])
66 |
67 | ;;; return an ast tree as {tag=tag, nodes=[node1, node2, ...]} form
68 | ;;; also "slice unquote" Multi ast
69 | (defn -to-ml-table-pass-1 [tree]
70 | (cond [(instance? (, list tuple) tree)
71 | (do
72 | (def nodes [])
73 | (for [node tree]
74 | (def elt (-to-ml-table-pass-1 node))
75 | ;; slice unquoting Multi ast
76 | (if (and (instance? dict elt)
77 | (= (get elt "tag") "Multi"))
78 | (+= nodes (get elt "nodes"))
79 | (.append nodes elt)))
80 | nodes)]
81 | [(or (instance? Real tree)
82 | (string? tree))
83 | tree]
84 | [(instance? ASTNode tree)
85 | (do
86 | (def res {"tag" tree.tag})
87 | (def nodes (-to-ml-table-pass-1 tree.nodes))
88 | (.update res {"nodes" nodes})
89 | res)]))
90 |
91 | (defn -to-ml-table-pass-2 [tree]
92 | (cond [(instance? (, list tuple) tree)
93 | (lt->dict (list-comp (-to-ml-table-pass-2 node)
94 | [node tree]))]
95 | [(instance? dict tree)
96 | (let [[res {"tag" (get tree "tag")}]
97 | [nodes (-to-ml-table-pass-2 (get tree "nodes"))]]
98 | (.update res nodes)
99 | res)]
100 | [(or (instance? Real tree)
101 | (string? tree))
102 | tree]))
103 |
104 | (defn to-ml-table [tree]
105 | (-> tree
106 | -to-ml-table-pass-1
107 | -to-ml-table-pass-2))
108 |
109 |
110 | (defclass Stat [ASTNode])
111 |
112 | (defclass Expr [ASTNode])
113 |
114 | (defclass LHS [Expr])
115 |
116 | (defclass Apply [Stat Expr])
117 |
118 | ;;; (simple) test functions
119 | (defn expr? [o]
120 | (instance? Expr o))
121 |
122 | (defn stat? [o]
123 | (instance? Stat o))
124 |
125 | (defn block? [o]
126 | (and (coll? o)
127 | (every? stat? o)))
128 |
129 |
130 | (defclass Nil [Expr]
131 | [[tag "Nil"]])
132 |
133 | (defclass Dots [Expr]
134 | [[tag "Dots"]])
135 |
136 | (defclass MLTrue [Expr]
137 | [[tag "True"]])
138 |
139 | (defclass MLFalse [Expr]
140 | [[tag "False"]])
141 |
142 | (defclass Number [Expr]
143 | [[tag "Number"]
144 | [--init--
145 | (fn [self value]
146 | (setv self.value value)
147 | (setv self.nodes [value])
148 | None)]
149 | [--repr--
150 | (fn [self]
151 | (% (.gen-repr-template self) (get self.nodes 0)))]])
152 |
153 | (defclass String [Expr]
154 | [[tag "String"]
155 | [--init--
156 | (fn [self value]
157 | (setv self.value value)
158 | (setv self.nodes [value])
159 | None)]
160 | [--repr--
161 | (fn [self]
162 | (% (.gen-repr-template self) (get self.nodes 0)))]])
163 |
164 | (defclass Function [Expr]
165 | [[tag "Function"]
166 | [--init--
167 | (fn [self args body]
168 | (setv self.args args)
169 | (setv self.body body)
170 | nil)]
171 | [nodes
172 | (with-decorator property
173 | (defn nodes [self]
174 | [self.args self.body]))]])
175 |
176 | ;;; for metalua ast Table internal usage
177 | (defclass -Pair [ASTNode]
178 | [[tag "Pair"]
179 | [--init--
180 | (fn [self p1 p2]
181 | (setv self.nodes [p1 p2])
182 | nil)]])
183 |
184 | (defclass Table [Expr]
185 | [[tag "Table"]
186 | [--init--
187 | (fn [self array-part hash-part]
188 | (setv self.array-part array-part)
189 | (setv self.hash-part hash-part)
190 | nil)]
191 | [nodes
192 | (with-decorator property
193 | (defn nodes [self]
194 | (let [[array-list (if (nil? self.array-part)
195 | []
196 | self.array-part)]
197 | [hash-part (if (nil? self.hash-part)
198 | []
199 | (list-comp (-Pair key value)
200 | [(, key value) (.items self.hash-part)]))]]
201 | (+ array-list hash-part))))]])
202 |
203 | (defclass Op [Expr]
204 | [[tag "Op"]
205 | [--init--
206 | (fn [self opid e1 &optional [e2 nil]]
207 | (setv self.opid opid)
208 | (setv self.e1 e1)
209 | (setv self.e2 e2)
210 | nil)]
211 | [nodes
212 | (with-decorator property
213 | (defn nodes [self]
214 | (let [[ret [self.opid self.e1]]]
215 | (if self.e2
216 | (+ ret [self.e2])
217 | ret))))]])
218 |
219 | (defclass Paren [Expr]
220 | [[tag "Paren"]
221 | [--init--
222 | (fn [self expr]
223 | (setv self.nodes [expr])
224 | nil)]])
225 |
226 | ;;; not a metalua AST, just for multiple assignment/return
227 | (defclass Multi [Expr]
228 | [[tag "Multi"]
229 | [--init--
230 | (fn [self exprs]
231 | (setv self.exprs exprs)
232 | nil)]
233 | [nodes
234 | (with-decorator property
235 | (fn [self]
236 | (def ret (cond [(coll? self.exprs)
237 | self.exprs]
238 | [true
239 | [self.exprs]]))
240 | ret))]
241 | [count
242 | (fn [self]
243 | (len self.nodes))]])
244 |
245 | (defn convert-to-multi [node]
246 | (cond [(instance? Multi node)
247 | node]
248 | [(instance? Expr node)
249 | (Multi [node])]
250 | [(coll? node)
251 | (Multi node)]))
252 |
253 | ;;; lhs
254 | (defclass Id [LHS]
255 | [[tag "Id"]
256 | [--init--
257 | (fn [self name]
258 | (setv self.name name)
259 | nil)]
260 | [--repr--
261 | (fn [self]
262 | (% (.gen-repr-template self) self.name))]
263 | [nodes
264 | (with-decorator property
265 | (fn [self]
266 | [self.name]))]])
267 |
268 | (defclass Index [LHS]
269 | [[tag "Index"]
270 | [--init--
271 | (fn [self expr1 expr2]
272 | (setv self.nodes [expr1 expr2])
273 | nil)]])
274 |
275 | ;;; Statements
276 | (defclass Do [Stat]
277 | [[tag "Do"]
278 | [--init--
279 | (fn [self stats]
280 | (setv self.nodes stats)
281 | nil)]])
282 |
283 | (defclass Set [Stat]
284 | [[tag "Set"]
285 | [--init--
286 | (fn [self lhss rhss]
287 | (setv self.lhss (convert-to-multi lhss))
288 | (setv self.rhss (convert-to-multi rhss))
289 | nil)]
290 | [nodes
291 | (with-decorator property
292 | (fn [self]
293 | [[self.lhss] [self.rhss]]))]])
294 |
295 | (defclass If [Stat]
296 | [[tag "If"]
297 | [--init--
298 | (fn [self expr1 block1 &rest rest]
299 | (setv self.nodes [expr1 block1])
300 | (+= self.nodes rest)
301 | nil)]])
302 |
303 | (defclass Fornum [Stat]
304 | [[tag "Fornum"]
305 | [--init--
306 | (fn [self target expr-list body]
307 | (setv self.nodes (+ [target] expr-list [body]))
308 | nil)]])
309 |
310 | (defclass Forin [Stat]
311 | [[tag "Forin"]
312 | [--init--
313 | (fn [self target iterable body]
314 | (setv self.target (convert-to-multi target))
315 | (setv self.iterable iterable)
316 | (setv self.body body)
317 | nil)]
318 | [nodes
319 | (with-decorator property
320 | (fn [self]
321 | [[self.target] [self.iterable] self.body]))]])
322 |
323 | (defclass Local [Stat]
324 | [[tag "Local"]
325 | [--init--
326 | (fn [self lhss &optional [rhss nil]]
327 | (setv self.lhss (convert-to-multi lhss))
328 | (setv self.rhss (convert-to-multi rhss))
329 | nil)]
330 | [nodes
331 | (with-decorator property
332 | (fn [self]
333 | (def lhss-nodes [self.lhss])
334 | (def rhss-nodes (if (nil? self.rhss)
335 | []
336 | [self.rhss]))
337 | [lhss-nodes rhss-nodes]))]])
338 |
339 | (defclass Return [Stat]
340 | [[tag "Return"]
341 | [--init--
342 | (fn [self return-exprs]
343 | (if (coll? return-exprs)
344 | (setv self.nodes return-exprs)
345 | (setv self.nodes [return-exprs]))
346 | nil)]])
347 |
348 | ;;; Apply
349 | (defclass Call [Apply]
350 | [[tag "Call"]
351 | [--init--
352 | (fn [self func args]
353 | (when (instance? Function func)
354 | (setv func (Paren func)))
355 | (setv self.func func)
356 | (setv self.args args)
357 | nil)]
358 | [nodes
359 | (with-decorator property
360 | (defn nodes [self]
361 | (+ [self.func] self.args)))]])
362 |
363 | (defclass Invoke [Apply]
364 | [[tag "Invoke"]
365 | [--init--
366 | (fn [self obj method args]
367 | (setv self.obj obj)
368 | (setv self.method method)
369 | (setv self.args args)
370 | nil)]
371 | [nodes
372 | (with-decorator property
373 | (defn nodes [self]
374 | (+ [self.obj self.method] self.args)))]])
375 |
376 | ;;; test
377 | ;; (import [lua [init-lua lua lua-astc -dict->ltable]])
378 | ;; (setv test-expr (Local [(Id "a") (Id "b")] [(Number 1)]))
379 | ;; (setv test-expr2 (Set [(Id "a") (Id "b")] [(Number 1) (Number 3)]))
380 | ;; (setv test-3 (If (MLTrue) [test-expr2] [test-expr2]))
381 | ;; (print (.to-ml-table test-expr))
382 | ;; ;(print (.ast-to-src lua-astc lua-astc (.table-from lua (-dict->ltable lua (.to-ml-table test-expr2)))))
383 | ;; ;(print (.ast-to-src lua-astc lua-astc (.table-from lua (-dict->ltable lua (.to-ml-table test-3)))))
384 |
385 | ;; (print (.ast-to-src lua-astc lua-astc (.table-from lua (-dict->ltable lua (.to-ml-table (Index (Index (Id "a") (Number 1)) (Id "b")))))))
386 |
387 |
388 |
389 |
--------------------------------------------------------------------------------
/hua/tlcode.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | This file implements the code generator for Typed Lua
3 | ]]
4 | local tlcode = {}
5 |
6 | local code_block, code_stm, code_exp, code_var
7 | local code_explist, code_varlist, code_fieldlist, code_idlist
8 |
9 | local function spaces (n)
10 | return string.rep(" ", 2 * n)
11 | end
12 |
13 | local function indent (s, n)
14 | return spaces(n) .. s
15 | end
16 |
17 | local function iscntrl (x)
18 | if (x >= 0 and x <= 31) or (x == 127) then return true end
19 | return false
20 | end
21 |
22 | local function isprint (x)
23 | return not iscntrl(x)
24 | end
25 |
26 | local function fix_str (str)
27 | local new_str = ""
28 | for i=1,string.len(str) do
29 | char = string.byte(str, i)
30 | if char == 34 then new_str = new_str .. string.format("\\\"")
31 | elseif char == 92 then new_str = new_str .. string.format("\\\\")
32 | elseif char == 7 then new_str = new_str .. string.format("\\a")
33 | elseif char == 8 then new_str = new_str .. string.format("\\b")
34 | elseif char == 12 then new_str = new_str .. string.format("\\f")
35 | elseif char == 10 then new_str = new_str .. string.format("\\n")
36 | elseif char == 13 then new_str = new_str .. string.format("\\r")
37 | elseif char == 9 then new_str = new_str .. string.format("\\t")
38 | elseif char == 11 then new_str = new_str .. string.format("\\v")
39 | else
40 | if isprint(char) then
41 | new_str = new_str .. string.format("%c", char)
42 | else
43 | new_str = new_str .. string.format("\\%03d", char)
44 | end
45 | end
46 | end
47 | return new_str
48 | end
49 |
50 | local op = { add = " + ",
51 | sub = " - ",
52 | mul = " * ",
53 | idiv = " // ",
54 | div = " / ",
55 | mod = " % ",
56 | pow = " ^ ",
57 | concat = " .. ",
58 | eq = " == ",
59 | lt = " < ",
60 | le = " <= ",
61 | bor = "|",
62 | bxor = "~",
63 | band = "&",
64 | shl = "<<",
65 | shr = ">>",
66 | ["and"] = " and ",
67 | ["or"] = " or ",
68 | ["not"] = "not ",
69 | unm = "-",
70 | bnot = "~",
71 | len = "#" }
72 |
73 | local function code_call (call, fmt)
74 | local l = {}
75 | for k = 2, #call do
76 | l[k - 1] = code_exp(call[k], fmt)
77 | end
78 | return code_exp(call[1], fmt) .. "(" .. table.concat(l, ",") .. ")"
79 | end
80 |
81 | local function code_invoke (invoke, fmt)
82 | local l = {}
83 | for k = 3, #invoke do
84 | l[k - 2] = code_exp(invoke[k], fmt)
85 | end
86 | local str = code_exp(invoke[1], fmt)
87 | str = str .. ":" .. invoke[2][1]
88 | str = str .. "(" .. table.concat(l, ",") .. ")"
89 | return str
90 | end
91 |
92 | local function code_parlist (parlist, fmt)
93 | local l = {}
94 | local len = #parlist
95 | local is_vararg = false
96 | if len > 0 and parlist[len].tag == "Dots" then
97 | is_vararg = true
98 | len = len - 1
99 | end
100 | local k = 1
101 | for k=1, len do
102 | l[k] = code_var(parlist[k], fmt)
103 | end
104 | if is_vararg then
105 | table.insert(l, "...")
106 | end
107 | return table.concat(l, ", ")
108 | end
109 |
110 | local function code_fieldlist (fieldlist, fmt)
111 | local l = {}
112 | for k, v in ipairs(fieldlist) do
113 | if v.tag == "Pair" then
114 | l[k] = "[" .. code_exp(v[1], fmt) .. "] = " .. code_exp(v[2], fmt)
115 | else
116 | l[k] = code_exp(v, fmt)
117 | end
118 | end
119 | return table.concat(l, ", ")
120 | end
121 |
122 | function code_var (var, fmt)
123 | local tag = var.tag
124 | if tag == "Id" then
125 | return var[1]
126 | elseif tag == "Index" then
127 | if var[1].tag == "Id" and var[1][1] == "_ENV" and var[2].tag == "String" then
128 | local v = { tag = "Id", [1] = var[2][1] }
129 | return code_exp(v, fmt)
130 | else
131 | return code_exp(var[1], fmt) .. "[" .. code_exp(var[2], fmt) .. "]"
132 | end
133 | else
134 | error("trying to generate code for a variable, but got a " .. tag)
135 | end
136 | end
137 |
138 | function code_varlist (varlist, fmt)
139 | local l = {}
140 | for k, v in ipairs(varlist) do
141 | l[k] = code_var(v, fmt)
142 | end
143 | return table.concat(l, ", ")
144 | end
145 |
146 | function code_exp (exp, fmt)
147 | local tag = exp.tag
148 | if tag == "Nil" then
149 | return "nil"
150 | elseif tag == "Dots" then
151 | return "..."
152 | elseif tag == "True" then
153 | return "true"
154 | elseif tag == "False" then
155 | return "false"
156 | elseif tag == "Number" then
157 | return tostring(exp[1])
158 | elseif tag == "String" then
159 | return '"' .. fix_str(exp[1]) .. '"'
160 | elseif tag == "Function" then
161 | local str = "function ("
162 | str = str .. code_parlist(exp[1], fmt) .. ")\n"
163 | if not exp[3] then
164 | str = str .. code_block(exp[2], fmt) .. indent("end", fmt)
165 | else
166 | str = str .. code_block(exp[3], fmt) .. indent("end", fmt)
167 | end
168 | return str
169 | elseif tag == "Table" then
170 | local str = "{" .. code_fieldlist(exp, fmt) .. "}"
171 | return str
172 | elseif tag == "Op" then
173 | local str = ""
174 | if exp[3] then
175 | if _VERSION == "Lua 5.3" then
176 | if exp[2].tag == "Call" and exp[2][1].tag == "Index" and
177 | exp[2][1][1].tag == "Id" and exp[2][1][1][1] == "_ENV" and
178 | exp[2][1][2].tag == "String" and exp[2][1][2][1] == "type" and
179 | exp[3].tag == "String" and exp[3][1] == "integer" then
180 | str = "math."
181 | end
182 | end
183 | str = str .. code_exp(exp[2], fmt) .. op[exp[1]] .. code_exp(exp[3], fmt)
184 | else
185 | str = str .. op[exp[1]] .. "(" .. code_exp(exp[2], fmt) .. ")"
186 | end
187 | return str
188 | elseif tag == "Paren" then
189 | local str = "(" .. code_exp(exp[1], fmt) .. ")"
190 | return str
191 | elseif tag == "Call" then
192 | return code_call(exp, fmt)
193 | elseif tag == "Invoke" then
194 | return code_invoke(exp, fmt)
195 | elseif tag == "Id" or
196 | tag == "Index" then
197 | return code_var(exp, fmt)
198 | else
199 | error("trying to generate code for a expression, but got a " .. tag)
200 | end
201 | end
202 |
203 | function code_explist (explist, fmt)
204 | local l = {}
205 | for k, v in ipairs(explist) do
206 | l[k] = code_exp(v, fmt)
207 | end
208 | return table.concat(l, ", ")
209 | end
210 |
211 | function code_stm (stm, fmt)
212 | local tag = stm.tag
213 | if tag == "Do" then
214 | local str = indent("do\n", fmt) .. code_block(stm, fmt) .. indent("end", fmt)
215 | return str
216 | elseif tag == "Set" then
217 | local str = spaces(fmt)
218 | str = str .. code_varlist(stm[1], fmt) .. " = " .. code_explist(stm[2], fmt)
219 | return str
220 | elseif tag == "While" then
221 | local str = indent("while ", fmt) .. code_exp(stm[1], 0) .. " do\n"
222 | str = str .. code_block(stm[2], fmt) .. indent("end", fmt)
223 | return str
224 | elseif tag == "Repeat" then
225 | local str = indent("repeat\n", fmt)
226 | str = str .. code_block(stm[1], fmt)
227 | str = str .. indent("until ", fmt)
228 | str = str .. code_exp(stm[2], fmt)
229 | return str
230 | elseif tag == "If" then
231 | local str = indent("if ", fmt) .. code_exp(stm[1], 0) .. " then\n"
232 | str = str .. code_block(stm[2], fmt)
233 | local len = #stm
234 | if len % 2 == 0 then
235 | for k=3, len, 2 do
236 | str = str .. indent("elseif ", fmt) .. code_exp(stm[k], 0) .. " then\n"
237 | str = str .. code_block(stm[k+1], fmt)
238 | end
239 | else
240 | for k=3, len-1, 2 do
241 | str = str .. indent("elseif ", fmt) .. code_exp(stm[k], 0) .. " then\n"
242 | str = str .. code_block(stm[k+1], fmt)
243 | end
244 | str = str .. indent("else\n", fmt)
245 | str = str .. code_block(stm[len], fmt)
246 | end
247 | str = str .. indent("end", fmt)
248 | return str
249 | elseif tag == "Fornum" then
250 | local str = indent("for ", fmt)
251 | str = str .. code_var(stm[1], fmt) .. " = " .. code_exp(stm[2], fmt)
252 | str = str .. ", " .. code_exp(stm[3], fmt)
253 | if stm[5] then
254 | str = str .. ", " .. code_exp(stm[4], fmt) .. " do\n"
255 | str = str .. code_block(stm[5], fmt)
256 | else
257 | str = str .. " do\n" .. code_block(stm[4], fmt)
258 | end
259 | str = str .. indent("end", fmt)
260 | return str
261 | elseif tag == "Forin" then
262 | local str = indent("for ", fmt)
263 | str = str .. code_varlist(stm[1], fmt) .. " in "
264 | str = str .. code_explist(stm[2], fmt) .. " do\n"
265 | str = str .. code_block(stm[3], fmt)
266 | str = str .. indent("end", fmt)
267 | return str
268 | elseif tag == "Local" then
269 | local str = indent("local ", fmt) .. code_varlist(stm[1], fmt)
270 | if #stm[2] > 0 then
271 | str = str .. " = " .. code_explist(stm[2], fmt)
272 | end
273 | return str
274 | elseif tag == "Localrec" then
275 | local str = indent("local function ", fmt) .. code_var(stm[1][1], fmt)
276 | str = str .. " (" .. code_parlist(stm[2][1][1], fmt) .. ")\n"
277 | if not stm[2][1][3] then
278 | str = str .. code_block(stm[2][1][2], fmt) .. indent("end", fmt)
279 | else
280 | str = str .. code_block(stm[2][1][3], fmt) .. indent("end", fmt)
281 | end
282 | return str
283 | elseif tag == "Goto" then
284 | local str = indent("goto ", fmt) .. stm[1]
285 | return str
286 | elseif tag == "Label" then
287 | local str = indent("::", fmt) .. stm[1] .. "::"
288 | return str
289 | elseif tag == "Return" then
290 | local str = indent("return ", fmt) .. code_explist(stm, fmt)
291 | return str
292 | elseif tag == "Break" then
293 | return indent("break", fmt)
294 | elseif tag == "Call" then
295 | return indent(code_call(stm, fmt), fmt)
296 | elseif tag == "Invoke" then
297 | return indent(code_invoke(stm, fmt), fmt)
298 | elseif tag == "Interface" then
299 | return ""
300 | else
301 | error("tyring to generate code for a statement, but got " .. tag)
302 | end
303 | end
304 |
305 | function code_block (block, fmt)
306 | local l = {}
307 | for k, v in ipairs(block) do
308 | l[k] = code_stm(v, fmt + 1)
309 | end
310 | return table.concat(l, "\n") .. "\n"
311 | end
312 |
313 | function tlcode.generate (ast)
314 | assert(type(ast) == "table")
315 | return code_block(ast, -1)
316 | end
317 |
318 | return tlcode
319 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright (c) 2012, 2013 Paul Tagliamonte
3 | #
4 | # Permission is hereby granted, free of charge, to any person obtaining a
5 | # copy of this software and associated documentation files (the "Software"),
6 | # to deal in the Software without restriction, including without limitation
7 | # the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 | # and/or sell copies of the Software, and to permit persons to whom the
9 | # Software is furnished to do so, subject to the following conditions:
10 | #
11 | # The above copyright notice and this permission notice shall be included in
12 | # all copies or substantial portions of the Software.
13 | #
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | # DEALINGS IN THE SOFTWARE.
21 |
22 | import os
23 | import re
24 | import sys
25 |
26 | from setuptools import find_packages, setup
27 |
28 | PKG = "hua"
29 | VERSIONFILE = os.path.join(PKG, "version.py")
30 | verstr = "unknown"
31 | try:
32 | verstrline = open(VERSIONFILE, "rt").read()
33 | except EnvironmentError:
34 | pass # Okay, there is no version file.
35 | else:
36 | VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]"
37 | mo = re.search(VSRE, verstrline, re.M)
38 | if mo:
39 | __version__ = mo.group(1)
40 | else:
41 | msg = "if %s.py exists, it is required to be well-formed" % VERSIONFILE
42 | raise RuntimeError(msg)
43 |
44 | long_description = """Hua is a Lisp to lua compiler. It gives lua
45 | simple and powerful meta programming ability."""
46 |
47 | install_requires = ['hy>=0.11.0', 'lupa>=1.1']
48 | if sys.version_info[:2] < (2, 7):
49 | install_requires.append('argparse>=1.2.1')
50 | install_requires.append('importlib>=1.0.2')
51 | if os.name == 'nt':
52 | install_requires.append('pyreadline==2.0')
53 |
54 | setup(
55 | name=PKG,
56 | #version=__version__,
57 | version="0.0.1",
58 | install_requires=install_requires,
59 | entry_points={
60 | 'console_scripts': [
61 | 'hua = hua.cmdline:hua_main',
62 | 'huac = hua.cmdline:huac_main',
63 | ]
64 | },
65 | packages=find_packages(exclude=['tests*']),
66 | package_data={
67 | 'hua.core': ['*.hy', '*.hua'],
68 | },
69 | author="Zhao Shenyang",
70 | author_email="dev@zsy.im",
71 | long_description=long_description,
72 | description='Lisp to lua compiler.',
73 | license="Expat",
74 | url="",
75 | platforms=['any'],
76 | classifiers=[
77 | "Development Status :: 2 - Pre-Alpha",
78 | "Intended Audience :: Developers",
79 | "License :: DFSG approved",
80 | "License :: OSI Approved :: MIT License", # Really "Expat". Ugh.
81 | "Operating System :: OS Independent",
82 | "Programming Language :: Lisp",
83 | "Programming Language :: lua",
84 | "Programming Language :: Python",
85 | "Programming Language :: Python :: 2",
86 | "Programming Language :: Python :: 2.6",
87 | "Programming Language :: Python :: 2.7",
88 | "Programming Language :: Python :: 3",
89 | "Programming Language :: Python :: 3.3",
90 | "Programming Language :: Python :: 3.4",
91 | "Topic :: Software Development :: Code Generators",
92 | "Topic :: Software Development :: Compilers",
93 | "Topic :: Software Development :: Libraries",
94 | ]
95 | )
96 |
--------------------------------------------------------------------------------
/tests/native_tests/core.hua:
--------------------------------------------------------------------------------
1 | (require-macro hua.core.initialize)
2 | (--hua-initialize--)
3 |
4 | (hua-import luaunit)
5 |
6 | (def assert-equal luaunit.assertEquals)
7 |
8 | (def m {})
9 |
10 | (setv m.test-def
11 | (fn []
12 | (def x 1)
13 | (def y 1)
14 | (assert-equal x y)
15 | (def x (def y (fn [x] 9)))
16 | (assert-equal (x y) 9)
17 | (assert-equal (y x) 9)))
18 |
19 | (setv m.test-setv
20 | (fn []
21 | (def x nil)
22 | (def y nil)
23 | (setv x (setv y 12))
24 | (assert-equal x 12)
25 | (assert-equal y 12)))
26 |
27 | (return m)
28 |
--------------------------------------------------------------------------------
/tests/native_tests/defclass.hua:
--------------------------------------------------------------------------------
1 | (require-macro hua.core.initialize)
2 | (--hua-initialize--)
3 |
4 | (hua-import luaunit)
5 |
6 | (def assert-equal luaunit.assertEquals)
7 |
8 | (def m {})
9 |
10 | (setv m.test-defclass-attrs
11 | (fn []
12 | (defclass A []
13 | [[--init (fn [self] self)]
14 | [x 42]])
15 | (assert-equal A.x 42)
16 | (assert-equal (get (A) "x") 42)))
17 |
18 | (setv m.test-defclass-attrs-fn
19 | (fn []
20 | (defclass B []
21 | [[--init (fn [self] self)]
22 | [x 42]
23 | [y (fn [self value]
24 | (+ self.x value))]])
25 | (assert-equal B.x 42)
26 | (assert-equal (.y (B) 5) 47)
27 | (let [[b (B)]]
28 | (setv B.x 0)
29 | (assert-equal (.y B 1) 1))))
30 |
31 | (return m)
32 |
--------------------------------------------------------------------------------
/tests/native_tests/language.hua:
--------------------------------------------------------------------------------
1 | (require-macro hua.core.initialize)
2 | (--hua-initialize--)
3 |
4 | (hua-import luaunit)
5 |
6 | (def assert-equal luaunit.assertEquals)
7 | (def assert-true luaunit.assertTrue)
8 | (def assert-false luaunit.assertFalse)
9 |
10 | (def m {})
11 |
12 | (setv m.test-lists
13 | (fn []
14 | (assert-equal [1 2 3 4]
15 | {1 1 2 2 3 3 4 4})))
16 |
17 | (setv m.test-dicts
18 | (fn []
19 | (assert-equal {1 2 3 4}
20 | {3 4 1 2})
21 | (assert-equal {1 2 3 4}
22 | {1 (+ 1 1) 3 (+ 2 2)})))
23 |
24 | (setv m.test-setv-get
25 | (fn []
26 | (def foo [1 2 3])
27 | (setv (get foo 1) 12)
28 | (assert-equal (get foo 1) 12)))
29 |
30 | (setv m.test-for-loop
31 | (fn []
32 | (def count 0)
33 | (for [i [1 5]]
34 | (+= count i))
35 | (assert-equal count 15)
36 | (setv count 0)
37 | (for [i [1 5]
38 | (, _ j) (ipairs [1 2 3 4 5])]
39 | (setv count (+ count i j)))
40 | (assert-equal count 150)))
41 |
42 | (setv m.test-not
43 | (fn []
44 | (assert-true (not (= 1 2)))
45 | (assert-true (= true (not false)))
46 | (assert-true (= false (not 42)))))
47 |
48 | (setv m.test-noteq
49 | (fn []
50 | (assert-true (!= 2 3))))
51 |
52 | (setv m.test-numops
53 | (fn []
54 | (assert-true (> 5 4 3 2 1))
55 | (assert-true (< 1 2 3 4 5))
56 | (assert-true (<= 5 5 5 5))
57 | (assert-true (>= 5 5 5 5))))
58 |
59 | (setv m.test-branching
60 | (fn []
61 | "NATIVE: test if branching"
62 | (if true
63 | (assert (= 1 1))
64 | (assert (= 2 1)))))
65 |
66 | (setv m.test-branching
67 | (fn []
68 | (if true
69 | (assert-true (= 1 1))
70 | (assert-true (= 2 1)))))
71 |
72 |
73 | (setv m.test-branching-with-do
74 | (fn []
75 | (if false
76 | (assert-true (= 2 1))
77 | (do
78 | (assert-true (= 1 1))
79 | (assert-true (= 1 1))
80 | (assert-true (= 1 1))))))
81 |
82 | (setv m.test-branching-expr-count-with-do
83 | (fn []
84 | (setv counter 0)
85 | (if false
86 | (assert-true (= 2 1))
87 | (do
88 | (setv counter (+ counter 1))
89 | (setv counter (+ counter 1))
90 | (setv counter (+ counter 1))))
91 | (assert-true (= counter 3))))
92 |
93 | (setv m.test-cond
94 | (fn []
95 | "NATIVE: test if cond sorta works."
96 | (cond
97 | [(= 1 2) (assert-true (= true false))]
98 | [(= nil nil) (assert-true (= true true))])))
99 |
100 |
101 | (setv m.test-index
102 | (fn []
103 | "NATIVE: Test that dict access works"
104 | ;; in lua you cannot write something like {1, 2, 3}[1]
105 | (def t1 {"one" "two"})
106 | (assert-equal (get t1 "one") "two")
107 | (assert-equal (get {"one" "two"} "one") "two")
108 |
109 | (def t2 [1 2 3 4 5])
110 | (assert-equal (get t2 2) 2)
111 | (assert-equal (get [1 2 3 4 5] 2) 2)
112 |
113 | (def t3 {"first" {"second" {"third" "level"}}})
114 | (assert-equal (get t3 "first" "second" "third")
115 | "level")
116 | (assert-equal (get {"first" {"second" {"third" "level"}}}
117 | "first" "second" "third")
118 | "level")
119 |
120 | (assert-true (= (get ((fn [] {"first" {"second" {"third" "level"}}}))
121 | "first" "second" "third")
122 | "level"))
123 |
124 | (def t4 {"first" {"second" {"third" "level"}}})
125 | (assert-true (= (get t4 ((fn [] "first")) "second" "third")
126 | "level"))
127 | ))
128 |
129 |
130 | (setv m.test-lambda
131 | (fn []
132 | "NATIVE: test lambda operator"
133 | (setv square (lambda [x] (* x x)))
134 | (assert-true (= 4 (square 2)))
135 | (setv lambda_list (lambda [test &rest args] [test args]))
136 | (assert-equal [1 [2 3]] (lambda_list 1 2 3))))
137 |
138 |
139 | (return m)
140 |
141 |
--------------------------------------------------------------------------------
/tests/native_tests/reader_macros.hua:
--------------------------------------------------------------------------------
1 | (require-macro hua.core.initialize)
2 | (--hua-initialize--)
3 |
4 | (hua-import luaunit)
5 |
6 | (def assert-equal luaunit.assertEquals)
7 |
8 | (require-macro "reader_macros")
9 |
10 | (def m {})
11 |
12 | (setv m.test-reader-macros
13 | (fn []
14 | (assert-equal #^"works" "works")
15 | (assert-equal #x10 16)))
16 |
17 | (return m)
18 |
--------------------------------------------------------------------------------
/tests/native_tests/reader_macros.hy:
--------------------------------------------------------------------------------
1 | (defreader ^ [expr]
2 | expr)
3 |
4 | (defreader x [expr]
5 | (int (str expr) 16))
6 |
--------------------------------------------------------------------------------
/tests/native_tests/runtest.sh:
--------------------------------------------------------------------------------
1 | huac *.hua
2 | lua testall.lua -v
3 |
--------------------------------------------------------------------------------
/tests/native_tests/testall.hua:
--------------------------------------------------------------------------------
1 | (require-macro hua.core.initialize)
2 | (--hua-initialize--)
3 |
4 | (hua-import luaunit)
5 |
6 | (setv TestCore (require "core"))
7 | (setv TestLanguage (require "language"))
8 | (setv TestReaderMacro (require "reader_macros"))
9 | (setv TestDefclass (require "defclass"))
10 |
11 | (os.exit (luaunit.LuaUnit.run))
12 |
--------------------------------------------------------------------------------