/examples/extempore_lang.scm

http://github.com/digego/extempore · Scheme · 978 lines · 386 code · 192 blank · 400 comment · 0 complexity · 741c81e7461f180f7159bab5a4404f53 MD5 · raw file

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;
  3. ;; A basic introduction to the Extempore Language and Compiler
  4. ;;
  5. ;; These examples are specific to Extempore lang
  6. ;; for versions of LLVM v3.0+
  7. ;;
  8. ;;
  9. ;; multiple a * 5
  10. ;; note that type infercing works out the type
  11. ;; of "a" and then using the inferred type
  12. ;; also works out the type of my-test-1
  13. ;; (i.e. argument type and return type)
  14. ;;
  15. ;; integer literals default to 64 bit integers
  16. (definec my-test-1
  17. (lambda (a)
  18. (* a 5)))
  19. ;; notice that the log view displays the type
  20. ;; of the closure we just compiled
  21. ;; [i64,i64]*
  22. ;; The square brackets define a closure type
  23. ;; The first type within the square braces is
  24. ;; the return type of the function (i64 for 64bit integer)
  25. ;; Any remaining types are function arguments
  26. ;; in this case another i64 (for 64bit integer)
  27. ;;
  28. ;; All closures are pointers. Pointer types are
  29. ;; represented (as in "C") with a "*" which trails
  30. ;; the base type.
  31. ;; So a pointer to a 64 bit integer would be "i64*"
  32. ;; A double pointer type would be "double*"
  33. ;; So a closure pointer type is "[...]*"
  34. ;; float literals default to doubles
  35. (definec my-test-1f
  36. (lambda (a)
  37. (* a 5.0)))
  38. ;; Again note the closures type in the logview
  39. ;; [double,double]*
  40. ;; a closure that returns a double and
  41. ;; taks a double as it's only argument
  42. ;; we can call these new closures like so
  43. ;; making sure we pass an integer for my-test-1
  44. (println (my-test-1 6)) ;; 30
  45. ;; and a real number for my-test-1f
  46. (println (my-test-1f 6.0)) ;; 30.0
  47. ;; you are free to recompile an existing closure
  48. ;; so we can change my-test-1 to
  49. (definec my-test-1
  50. (lambda (a)
  51. (/ a 5)))
  52. (println (my-test-1 30)) ; 30 / 5 = 6
  53. ;; note that the closures signature is still the same
  54. ;; as it was before. This is important because we are
  55. ;; NOT allowed to change an existing compiled closures
  56. ;; type signature.
  57. ;;
  58. ;; So we CANNOT do this
  59. ;(definec my-test-1
  60. ; (lambda (a)
  61. ; (/ a 5.0)))
  62. ;; Just remember that you are not currently allowed to redefine an
  63. ;; existing function to a new definition that requres a different type signature.
  64. ;; This is to protect against the situation where you have allready compiled
  65. ;; code which requires the current signature.
  66. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  67. ;; Because we are working with closures
  68. ;; we can "close" over free variables
  69. ;; in this example we close over power
  70. ;; to maintain state between calls
  71. ;;
  72. ;; increment power each call
  73. (definec my-test-2
  74. (let ((power 0))
  75. (lambda (x)
  76. (set! power (+ power 1)) ;; set! for closure mutation as per scheme
  77. (* x power))))
  78. ;; each modifies state
  79. (println (my-test-2 2)) ;; should = 2
  80. (println (my-test-2 2)) ;; should = 4
  81. (println (my-test-2 2)) ;; etc
  82. ;; Closures can of course return closures.
  83. ;; notice the type signature of this closure
  84. ;; as printed in the logview "[[i64,i64]*]*"
  85. ;; being a closure that returns a closure
  86. ;; the outer closure takes no arguments
  87. ;; and the return closure takes an i64 argument
  88. (definec my-test-3
  89. (lambda ()
  90. (lambda (x)
  91. (* x 3))))
  92. ;; let's try to make a generic incrementor
  93. ;;
  94. ;; here we run into trouble
  95. ;; because the type inferencer cannot infer a valid type
  96. ;; for i or inc because there are no numberic literals
  97. ;; to help in the validation process
  98. ;; THIS WOULD CAUSE AN ERROR!
  99. ;(definec my-inc-maker
  100. ; (lambda (i)
  101. ; (lambda (inc)
  102. ; (set! i (+ i inc))
  103. ; i)))
  104. ;; This makes sense - should "+" operate
  105. ;; on doubles or integers - who knows?
  106. ;; So the type inferencer justifiably complains
  107. ;;
  108. ;; What can we do about this ...
  109. ;; we need to help the compiler out by providing
  110. ;; some explicit type information
  111. ;;
  112. ;; We can do that by "typing" a variable.
  113. ;; Explicitly typing a variable means tagging
  114. ;; the symbol with a type separated by ":"
  115. ;;
  116. ;; Here are some examples
  117. ;; x:i64 = x is a 64 bit integer
  118. ;; y:double = y is a double
  119. ;; z:i32* = z is a pointer to a 32 bit integer
  120. ;; w:[i64,i64]* = w is a closure which takes an i64 and returns an i64
  121. ;; (remember that closures are ALWAYS pointers it is not
  122. ;; valid to have a closure type which is NOT a pointer)
  123. ;;
  124. ;; With this information in mind we can
  125. ;; fix the incrementor by explicitly typing 'i'
  126. (definec my-inc-maker
  127. (lambda (i:i64)
  128. (lambda (inc)
  129. (set! i (+ i inc))
  130. i)))
  131. ;; this solves our problem as the compiler
  132. ;; can now use i's type to infer inc and
  133. ;; therefore my-inc-maker.
  134. ;; now we have a different problem.
  135. ;; if we call my-inc-maker we expect to be
  136. ;; returned a closure. However Scheme does not
  137. ;; know anything about Extempore Lang closure types and therefore
  138. ;; has no way of using the returned data.
  139. ;; Instead it places the returned pointer
  140. ;; (remember a closure is a pointer)
  141. ;; into a generic Scheme cptr type.
  142. ;; All pointer types moving from Extempore Lang -> Scheme
  143. ;; are converted into generic Scheme cptr types. Scheme
  144. ;; knows that the type is a cptr but has no further information.
  145. ;;
  146. ;;
  147. ;; We are free to then pass that cptr back into another
  148. ;; compiled Extempore Lang function as an argument. When moving
  149. ;; from Scheme -> Extempore Lang the generic Scheme cptr is
  150. ;; automatically converted back into the explicit pointer type
  151. ;; required by Extempore Lang.
  152. ;; IMPORTANT!: This conversion is automatic and UNCHECKED so
  153. ;; it is your responsibility to ensure that Scheme cptr's point
  154. ;; to appropriate data (i.e. appropriate for the function be
  155. ;; called in Extempore Lang).
  156. ;; So let's build a function that excepts a closure returned from
  157. ;; my-inc-maker as an argument, as well as a suitable operand, and
  158. ;; apply the closure.
  159. ;; f is our incoming closure
  160. ;; and x is our operand
  161. ;; THIS WILL CAUSE AN ERROR
  162. ;(definec my-inc-maker-wrappert
  163. ; (lambda (f x) ; f and x are args
  164. ; (f x)))
  165. ;; oops can't resolve the type of "f"
  166. ;; fair enough really.
  167. ;; even if we give a type for "x"
  168. ;; we still can't tell what "f"'s
  169. ;; return type should be?
  170. ;; This also causes an error!
  171. ;(definec my-inc-maker-wrappert
  172. ; (lambda (f x:i64) ; f and x are args
  173. ; (f x)))
  174. ;; so we need to type f properly
  175. (definec my-inc-maker-wrapper
  176. (lambda (f:[i64,i64]* x)
  177. (f x)))
  178. ;; ok so now we can call my-inc-maker
  179. ;; which will return a closure
  180. ;; which scheme stores as a generic cptr
  181. (define myf (my-inc-maker 0))
  182. ;; and we can call my-in-maker-wrapper
  183. ;; to appy myf
  184. (println (my-inc-maker-wrapper myf 1)) ; 1
  185. (println (my-inc-maker-wrapper myf 1)) ; 2
  186. (println (my-inc-maker-wrapper myf 1)) ; 3 etc..
  187. ;; of course the wrapper is only required if you
  188. ;; need interaction with the scheme world.
  189. ;; otherwise you just call my-inc-maker directly
  190. ;; this avoids the wrapper completely
  191. (definec my-inc-test
  192. (let ((f (my-inc-maker 0)))
  193. (lambda ()
  194. (f 1))))
  195. (println (my-inc-test)) ; 1
  196. (println (my-inc-test)) ; 2
  197. (println (my-inc-test)) ; 3
  198. ;; hopefully you're getting the idea.
  199. ;; note that once we've compiled something
  200. ;; we can then use it any of our new
  201. ;; function definitions.
  202. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  203. ;;
  204. ;; Closures can be recursive
  205. ;;
  206. (definec my-test-4
  207. (lambda (a)
  208. (if (< a 1)
  209. (printf "done\n")
  210. (begin (printf "a: %lld\n" a)
  211. (my-test-4 (- a 1))))))
  212. (my-test-4 7)
  213. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  214. ;; a simple tuple example
  215. ;;
  216. ;; tuple types are represented as <type,type,type>*
  217. ;;
  218. ;; make and return a simple tuple
  219. (definec my-test-6
  220. (lambda ()
  221. (alloc <i64,double,i32>)))
  222. ;; logview shows [<i64,double,i32>*]*
  223. ;; i.e. a closure that takes no arguments
  224. ;; and returns the tuple <i64,double,i32>*
  225. ;; here's another tuple example
  226. ;; note that my-test-7's return type is inferred
  227. ;; by the tuple-reference index
  228. ;; (i.e. i64 being tuple index 0)
  229. (definec my-test-7
  230. (lambda ()
  231. (let ((a (alloc <i64,double>)) ; returns pointer to type <i64,double>
  232. (b 37)
  233. (c 6.4))
  234. (tuple-set! a 0 b) ;; set i64 to 64
  235. (tset! a 1 c) ;; set double to 6.4 - tset! is an alias for tuple-set!
  236. (printf "tuple:1 %lld::%f\n" (tuple-ref a 0) (tref a 1))
  237. ;; we can fill a tuple in a single call by using tfill!
  238. (tfill! a 77 77.7)
  239. (printf "tuple:2 %lld::%f\n" (tuple-ref a 0) (tuple-ref a 1))
  240. (tuple-ref a 0)))) ;; return first element which is i64
  241. ;; should be 64 as we return the
  242. ;; first element of the tuple
  243. (println (my-test-7)) ; 77
  244. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  245. ;; some array code with *casting*
  246. ;; this function returns void
  247. (definec my-test-8
  248. (lambda ()
  249. (let ((v1 (alloc |5,float|))
  250. (v2 (alloc |5,float|))
  251. (i 0)
  252. (k 0))
  253. (dotimes (i 5)
  254. ;; random returns double so "truncate" to float
  255. ;; which is what v expects
  256. (array-set! v1 i (dtof (random))))
  257. ;; we can use the afill! function to fill an array
  258. (afill! v2 1.1 2.2 3.3 4.4 5.5)
  259. (dotimes (k 5)
  260. ;; unfortunately printf doesn't like floats
  261. ;; so back to double for us :(
  262. (printf "val: %lld::%f::%f\n" k
  263. (ftod (array-ref v1 k))
  264. (ftod (aref v2 k)))))))
  265. (my-test-8)
  266. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  267. ;; some crazy array code with
  268. ;; closures and arrays
  269. ;; try to figure out what this all does
  270. ;;
  271. ;; this example uses the array type
  272. ;; the pretty print for this type is
  273. ;; |num,type| num elements of type
  274. ;; |5,i64| is an array of 5 x i64
  275. ;;
  276. ;; An array is not a pointer type
  277. ;; i.e. |5,i64| cannot be bitcast to i64*
  278. ;;
  279. ;; However an array can be a pointer
  280. ;; i.e. |5,i64|* can be bitcast to i64*
  281. ;; i.e. |5,i64|** to i64** etc..
  282. ;;
  283. ;; make-array returns a pointer to an array
  284. ;; i.e. (make-array 5 i64) returns type |5,i64|*
  285. ;;
  286. ;; aref (array-ref) and aset! (array-set!)
  287. ;; can operate with either pointers to arrays or
  288. ;; standard pointers.
  289. ;;
  290. ;; in other words aref and aset! are happy
  291. ;; to work with either i64* or |5,i64|*
  292. (definec my-test-9
  293. (lambda (v:|5,i64|*)
  294. (let ((f (lambda (x)
  295. (* (array-ref v 2) x))))
  296. f)))
  297. (definec my-test-10
  298. (lambda (v:|5,[i64,i64]*|*)
  299. (let ((ff (aref v 0))) ; aref alias for array-ref
  300. (ff 5))))
  301. (definec my-test-11
  302. (lambda ()
  303. (let ((v (alloc |5,[i64,i64]*|)) ;; make an array of closures!
  304. (vv (alloc |5,i64|)))
  305. (array-set! vv 2 3)
  306. (aset! v 0 (my-test-9 vv)) ;; aset! alias for array-set!
  307. (my-test-10 v))))
  308. ;; try to guess the answer before you call this!!
  309. (println (my-test-11))
  310. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  311. ;; some conditionals
  312. (definec my-test-12
  313. (lambda (x:i64 y)
  314. (if (> x y)
  315. x
  316. y)))
  317. (println (my-test-12 12 13))
  318. (println (my-test-12 13 12))
  319. ;; returns boolean true
  320. (definec my-test-13
  321. (lambda (x:i64)
  322. (cond ((= x 1) (printf "A\n"))
  323. ((= x 2) (printf "B\n"))
  324. ((= x 3) (printf "C\n"))
  325. ((= x 4) (printf "D\n"))
  326. (else (printf "E\n")))
  327. #t))
  328. (my-test-13 1)
  329. (my-test-13 3)
  330. (my-test-13 100)
  331. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  332. ;; making a linear envelop generator
  333. ;; for signal processing and alike
  334. (definec envelope-segments
  335. (lambda (points:double* num-of-points:i64)
  336. (let ((lines (zone-alloc num-of-points [double,double]*))
  337. (k 0))
  338. (dotimes (k num-of-points)
  339. (let* ((idx (* k 2))
  340. (x1 (pointer-ref points (+ idx 0)))
  341. (y1 (pointer-ref points (+ idx 1)))
  342. (x2 (pointer-ref points (+ idx 2)))
  343. (y2 (pointer-ref points (+ idx 3)))
  344. (m (if (= 0.0 (- x2 x1)) 0.0 (/ (- y2 y1) (- x2 x1))))
  345. (c (- y2 (* m x2)))
  346. (l (lambda (time) (+ (* m time) c))))
  347. (pointer-set! lines k l)))
  348. lines)))
  349. (definec make-envelope
  350. (lambda (points:double* num-of-points)
  351. (let ((klines:[double,double]** (envelope-segments points num-of-points))
  352. (line-length num-of-points))
  353. (lambda (time)
  354. (let ((res -1.0)
  355. (k:i64 0))
  356. (dotimes (k num-of-points)
  357. (let ((line (pointer-ref klines k))
  358. (time-point (pointer-ref points (* k 2))))
  359. (if (or (= time time-point)
  360. (< time-point time))
  361. (set! res (line time)))))
  362. res)))))
  363. ;; make a convenience wrapper
  364. (definec env-wrap
  365. (let* ((points 3)
  366. (data (zone-alloc (* points 2) double)))
  367. (pointer-set! data 0 0.0) ;; point data
  368. (pset! data 1 0.0)
  369. (pset! data 2 2.0)
  370. (pset! data 3 1.0)
  371. (pset! data 4 4.0)
  372. (pset! data 5 0.0)
  373. (let ((f (make-envelope data points)))
  374. (lambda (time:double)
  375. (f time)))))
  376. (println (env-wrap 0.0)) ;; time 0.0 should give us 0.0
  377. (println (env-wrap 1.0)) ;; time 1.0 should give us 0.5
  378. (println (env-wrap 2.0)) ;; time 2.0 should be 1.0
  379. (println (env-wrap 2.5)) ;; going back down 0.75
  380. (println (env-wrap 4.0)) ;; to zero
  381. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  382. ;;
  383. ;; direct access to a closures environment
  384. ;;
  385. ;; it is possible to directly access a closures
  386. ;; environment in order to read or modify data
  387. ;; at runtime.
  388. ;;
  389. ;; You do this using a dot operator
  390. ;; To access an environment slot you use
  391. ;; closure.slot:type
  392. ;; So for example
  393. ;; (f.a:i32)
  394. ;; would return the 32bit integer symbol 'a'
  395. ;; from the closure 'f'
  396. ;;
  397. ;; To set an environment slot you just
  398. ;; add a value of the correct type
  399. ;; for example
  400. ;; (f.a:i32 565)
  401. ;; would set 'a' in 'f' to 565
  402. ;;
  403. ;; let's create a closure that capture's 'a'
  404. (definec my-test14
  405. (let ((a:i32 6))
  406. (lambda ()
  407. (printf "a:%d\n" a)
  408. a)))
  409. ;; calling my-test14 prints the value of a
  410. ;; and returns the bind to a (i.e. 6)
  411. (my-test14) ; 6
  412. ;; now let's create a new function
  413. ;; that calls my-test14 twice
  414. ;; once normally
  415. ;; then we directly set the closures 'a' binding
  416. ;; then call again
  417. ;;
  418. (definec my-test15
  419. (lambda (x:i32)
  420. (my-test14)
  421. (my-test14.a:i32 x)
  422. (my-test14)))
  423. ;; should print a:6 and a:9
  424. (my-test15 9) ; 9
  425. ;; now what happens if we pass 101
  426. ;; should print a:9 and a:101
  427. (my-test15 101) ; 101
  428. ;; of course this works just as well for
  429. ;; non-global closures
  430. (definec my-test16
  431. (lambda (a:i32)
  432. (let ((f (lambda ()
  433. (* 3 a))))
  434. f)))
  435. (definec my-test17
  436. (lambda ()
  437. (let ((f (my-test16 5)))
  438. (f.a:i32 7)
  439. (f))))
  440. (println (my-test17)) ;; 21
  441. ;; and you can get and set closures also!
  442. (definec my-test18
  443. (let ((f (lambda (x:i64) x)))
  444. (lambda ()
  445. (lambda (z)
  446. (f z)))))
  447. (definec my-test19
  448. (lambda ()
  449. (let ((t1 (my-test18))
  450. (t2 (my-test18)))
  451. ;; identity of 5
  452. (printf "%lld:%lld\n" (t1 5) (t2 5))
  453. (t1.f:[i64,i64]* (lambda (x:i64) (* x x)))
  454. ;; square of 5
  455. (printf "%lld:%lld\n" (t1 5) (t2 5))
  456. ;; cube of 5
  457. (my-test18.f:[i64,i64]* (lambda (y:i64) (* y y y)))
  458. (printf "%lld:%lld\n" (t1 5) (t2 5)))))
  459. (my-test19) ;; 5:5 > 25:25 > 125:125
  460. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  461. ;;
  462. ;; named types
  463. ;; we can name our own types using bind-type
  464. (bind-type mytype <i64,i64>)
  465. ;; which we can then use in place
  466. (definec my-test20
  467. (lambda (a:mytype*)
  468. (tref a 0)))
  469. ;; named types support recursion
  470. (bind-type i64list <i64,i64list*>)
  471. ;; Note the use of zone-alloc to allocate
  472. ;; enough zone memory to hold an i64list
  473. ;; zone-alloc returns a pointer to the
  474. ;; type that you ask it to allocate
  475. ;; pair is type i64list* in this case.
  476. ;;
  477. ;; You are responsible for cleaning up
  478. ;; this memory at some point in the future!
  479. ;; (i.e. cleaning up the memory zone that this
  480. ;; heap allocation was made into)
  481. (definec cons-i64
  482. (lambda (a:i64 b:i64list*)
  483. (let ((pair (zone-alloc i64list)))
  484. (tset! pair 0 a)
  485. (tset! pair 1 b)
  486. pair)))
  487. (definec car-i64
  488. (lambda (a:i64list*)
  489. (tref a 0)))
  490. (definec cdr-i64
  491. (lambda (a:i64list*)
  492. (tref a 1)))
  493. ;; print all i64's in list
  494. (definec my-test25
  495. (lambda (a:i64list*)
  496. (if (null? a)
  497. (begin (printf "done\n") 1)
  498. (begin (printf "%lld\n" (car-i64 a))
  499. (my-test25 (cdr-i64 a))))))
  500. ;; build a list (using cons) and then call my-test25
  501. (definec my-test26
  502. (lambda ()
  503. (let ((my-list (cons-i64 1 (cons-i64 2 (cons-i64 3 null)))))
  504. (my-test25 my-list))))
  505. (my-test26) ;; 1 > 2 > 3 > done
  506. ;; it can sometimes be helpful to allocate
  507. ;; a predefined tuple type on the stack
  508. ;; you can do this using allocate
  509. (bind-type vec3 <double,double,double>)
  510. ;; note that point is deallocated at the
  511. ;; end of the function call. You can
  512. ;; stack allocate (stack-alloc)
  513. ;; any valid type (i64 for example)
  514. (definec my-test27
  515. (lambda ()
  516. (let ((point (stack-alloc vec3)))
  517. (tset! point 0 0.0)
  518. (tset! point 1 -1.0)
  519. (tset! point 2 1.0)
  520. 1)))
  521. (println (my-test27)) ;; 1
  522. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  523. ;;
  524. ;; aref-ptr and tref-ptr
  525. ;;
  526. ;; aref-ptr and tref-ptr return a pointer to an element
  527. ;; just as aref and tref return elements aref-ptr and
  528. ;; tref-ptr return a pointer to those elements.
  529. ;; This allows you to do things like create an array
  530. ;; with an offset
  531. (definec my-test28
  532. (lambda ()
  533. (let ((arr (alloc |32,i64|))
  534. (arroff (aref-ptr arr 16))
  535. (i 0)
  536. (k 0))
  537. ;; load arr
  538. (dotimes (i 32) (aset! arr i i))
  539. (dotimes (k 16)
  540. (printf "index: %lld\tarr: %lld\tarroff: %lld\n"
  541. k (aref arr k) (pref arroff k))))))
  542. (my-test28) ;; print outs
  543. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  544. ;;
  545. ;; arrays
  546. ;; Extempore lang supports arrays as for first class
  547. ;; aggregate types (in other words as distinct from
  548. ;; a pointer).
  549. ;;
  550. ;; an array is made up of a size and a type
  551. ;; |32,i64| is an array of 32 elements of type i64
  552. ;;
  553. (bind-type tuple-with-array <double,|32,|4,i32||,float>)
  554. (definec my-test29
  555. (lambda ()
  556. (let ((tup (stack-alloc tuple-with-array))
  557. (t2 (stack-alloc |32,i64|)))
  558. (aset! t2 0 9)
  559. (tset! tup 2 5.5)
  560. (aset! (aref-ptr (tref-ptr tup 1) 0) 0 0)
  561. (aset! (aref-ptr (tref-ptr tup 1) 0) 1 1)
  562. (aset! (aref-ptr (tref-ptr tup 1) 0) 2 2)
  563. (printf "val: %lld %lld %f\n"
  564. (aref (aref-ptr (tref-ptr tup 1) 0) 1)
  565. (aref t2 0) (ftod (tref tup 2)))
  566. (aref (aref-ptr (tref-ptr tup 1) 0) 1))))
  567. (my-test29) ;; val: 1 9 5.5
  568. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  569. ;;
  570. ;; Global Variables
  571. ;;
  572. ;; You can allocate global variables using bind-val
  573. ;;
  574. (bind-val g-var-a i32 5)
  575. ;; increment g-var-a by inc
  576. ;; and return new value of g-var-a
  577. (definec my-test30
  578. (lambda (inc)
  579. (set! g-var-a (+ g-var-a inc))
  580. g-var-a))
  581. (println (my-test30 3)) ;; 8
  582. ;; you can bind any primitive type
  583. (bind-val g-var-b double 5.5)
  584. (bind-val g-var-c i1 0)
  585. ;; you can bind array types
  586. ;; and choose to either
  587. ;; a) assign a value to each element
  588. (bind-val g-var-a1 |5,i64| (list 1 2 3 4 5))
  589. ;; or b) assign a default value to all elements
  590. ;; for example initialize all 1024 double elements to 5.125
  591. (bind-val g-var-a2 |1024,double| 5.125)
  592. (definec test31
  593. (lambda ()
  594. (printf "a1[3]:%lld a2[55]:%f\n" (aref g-var-a1 3) (aref g-var-a2 55))
  595. 1))
  596. (test31)
  597. ;; finally you can use sys:make-cptr to allocate
  598. ;; memory to any ptr type you like. It is up to
  599. ;; you to however to ensure that you allocate an
  600. ;; appropriate amount of space.
  601. (bind-val g-var-d |4,i32|* (sys:make-cptr (* 4 4)))
  602. (bind-val g-var-e tuple-with-array* (sys:make-cptr (+ 8 (* 32 (* 4 4)) 4)))
  603. (definec test32
  604. (lambda ()
  605. (tset! g-var-e 0 11.0)
  606. (aset! g-var-d 0 55)
  607. (printf "%f :: %d\n" (tref g-var-e 0) (aref g-var-d 0))
  608. 1))
  609. (test32) ;; 11.000 :: 55
  610. (bind-val gvar-array |5,double| 0.0)
  611. (definec test33
  612. (lambda ()
  613. (aset! gvar-array 3 19.19)
  614. (aref gvar-array 3)))
  615. (println (test33)) ;; -> 19.19
  616. ;; End Of Tutorial
  617. (print)
  618. (println 'finished)
  619. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  620. ;;
  621. ;; Callbacks
  622. (definec test34
  623. (lambda (time:i64 count:i64)
  624. (printf "time: %lld:%lld\n" time count)
  625. (callback (+ time 1000) test34 (+ time 22050) (+ count 1))))
  626. (test34 (now) 0)
  627. ;; compiling this will stop the callbacks
  628. ;;
  629. ;; of course we need to keep the type
  630. ;; signature the same [void,i64,i64]*
  631. ;;
  632. (definec test34
  633. (lambda (time:i64 count:i64)
  634. void))
  635. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  636. ;;
  637. ;; some memzone tests
  638. (definec test35
  639. (lambda ()
  640. (let ((b (zalloc |5,double|)))
  641. (aset! b 0
  642. (memzone 1024
  643. (let ((a (zalloc |10,double|)))
  644. (aset! a 0 3.5)
  645. (aref a 0))))
  646. (let ((c (zalloc |9,i32|)))
  647. (aset! c 0 99)
  648. (aref b 0)))))
  649. (println (test35)) ;; 3.5
  650. (definec test36
  651. (lambda ()
  652. (memzone 1024
  653. (let ((k (zalloc |15,double|))
  654. (f (lambda (fa:|15,double|*)
  655. (memzone 1024
  656. (let ((a (zalloc |10,double|))
  657. (i 0))
  658. (dotimes (i 10)
  659. (aset! a i (* (aref fa i) (random))))
  660. a)))))
  661. (f k)))))
  662. (definec test37
  663. (lambda ()
  664. (let ((v (test36))
  665. (i 0))
  666. (dotimes (i 10) (printf "%lld:%f\n" i (aref v i))))))
  667. ;; should print all 0.0's
  668. (test37)
  669. (definec test38
  670. (lambda ()
  671. (memzone 1024 (* 44100 10)
  672. (let ((a (alloc |5,double|)))
  673. (aset! a 0 5.5)
  674. (aref a 0)))))
  675. (println (test38)) ;; 5.50000
  676. ;;
  677. ;; Large allocation of memory on BUILD (i.e. when the closure is created)
  678. ;; requires an optional argument (i.e. an amount of memory to allocate
  679. ;; specifically for closure creation)
  680. ;;
  681. ;; This memory is automatically free'd whenever you recompile the closure
  682. ;; (it will be destroyed and replaced by a new allocation of the
  683. ;; same amount or whatever new amount you have allocated for closure
  684. ;; compilation)
  685. ;;
  686. (definec test39 1000000
  687. (let ((k (zalloc |100000,double|)))
  688. (lambda ()
  689. (aset! k 0 1.0)
  690. (aref k 0))))
  691. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  692. ;;
  693. ;; Some data structures examples
  694. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  695. ;;
  696. ;; FIFO Queue for positive 64bit integers
  697. ;;
  698. ;; i8* is value --- list_t* is next
  699. (bind-type list_t <i64,list_t*>)
  700. ;; remove value from front of list
  701. (definec dequeue
  702. (lambda (queue:|2,list_t*|*)
  703. (let ((front (aref queue 0)))
  704. (if (null? front) -1
  705. (let ((val (tref front 0))
  706. (back (aref queue 1)))
  707. (aset! queue 0 (tref front 1))
  708. (if (= back front) (aset! queue 1 null))
  709. (free front)
  710. val)))))
  711. ;; add to the back of the list
  712. (definec enqueue
  713. (lambda (queue:|2,list_t*|* value:i64)
  714. (let ((tmp (halloc list_t))
  715. (front (aref queue 0))
  716. (back (aref queue 1)))
  717. (tset! tmp 0 value)
  718. (tset! tmp 1 null)
  719. (if (null? back) 1 (begin (tset! back 1 tmp) 1))
  720. (if (null? front) (aset! queue 0 tmp))
  721. (aset! queue 1 tmp) ;; set back to tmp
  722. 1)))
  723. (definec queue_test
  724. (lambda ()
  725. (let ((myqueue (salloc |2,list_t*|))
  726. (stuff (salloc |8,i64|))
  727. (i 0))
  728. ;; first we must set queue front and back to null
  729. (afill! myqueue null null)
  730. ;; initialize stuff array
  731. (dotimes (i 8) (aset! stuff i i))
  732. ;; what happens if we dequeue an empty queue (-1)
  733. (printf "dequeue 1: %lld\n" (dequeue myqueue))
  734. ;; add something to the queue
  735. (enqueue myqueue (aref stuff 1))
  736. ;; dequeue something
  737. (printf "dequeue 2: %lld\n" (dequeue myqueue))
  738. ;; back to nothing?
  739. (printf "dequeue 4: %lld\n" (dequeue myqueue))
  740. ;; etc..
  741. (enqueue myqueue (aref stuff 2))
  742. (printf "dequeue 5: %lld\n" (dequeue myqueue))
  743. (enqueue myqueue (aref stuff 3))
  744. (enqueue myqueue (aref stuff 4))
  745. (printf "dequeue 6: %lld\n" (dequeue myqueue))
  746. (printf "dequeue 7: %lld\n" (dequeue myqueue))
  747. (printf "dequeue 8: %lld\n" (dequeue myqueue))
  748. 1)))
  749. (queue_test)
  750. ;; Memory Usage In Extempore Lang
  751. ;; -------------------------------
  752. ;; Extempore supports three types of memory allocation: stack allocation,
  753. ;; heap alloation and zone allocation. The first two of these memory
  754. ;; allocation techniques should be familiar to anyone who has programmed
  755. ;; in C/C++. The third allocation type represents a type of middle ground
  756. ;; between these two extremes. Zone allocation in Extempore is in essence
  757. ;; a form of stack allocation whose scope is defined by the user.
  758. ;; Stack allocation in extempore is identical to stack allocation in C.
  759. ;; Stack allocation is made using the stack-alloc call (or salloc for
  760. ;; short). Stack allocations, as in C, are available only for the
  761. ;; duration of the function call. They are deallocated when the function
  762. ;; returns.
  763. ;; (definec ex1
  764. ;; (lambda ()
  765. ;; (let ((a (stack-alloc double)))
  766. ;; (aset! a 0 5.5)
  767. ;; (aref a 0))))
  768. ;; This example demonstrates a stack allocation of a single double (8
  769. ;; bytes) bound to the symbol a. The type returned by stack-alloc is
  770. ;; always a pointer to the memory allocated. In ex1 the instance 'a'
  771. ;; will be of type double* (a:double*). An optional integer argument
  772. ;; before the requested type results in a multiple allocation.
  773. ;; (bind-type vec3 <float,float,float>)
  774. ;; (definec ex2
  775. ;; (lambda ()
  776. ;; ;; calls that draw memory from the current zone
  777. ;; make-string (literal strings are constant heap allocations)
  778. ;; closures (i.e. lambda)
  779. ;; make-array
  780. ;; make-tuple
  781. ;; zone-alloc
  782. ;; ;; call that draw memory from the stack
  783. ;; stack-alloc
  784. ;; just about everything else
  785. ;; ;; calls that draw memory from the heap
  786. ;; heap-alloc