nydp 0.4.0 → 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +44 -0
  3. data/lib/lisp/core-010-precompile.nydp +13 -16
  4. data/lib/lisp/core-012-utils.nydp +21 -6
  5. data/lib/lisp/core-015-documentation.nydp +60 -19
  6. data/lib/lisp/core-017-builtin-dox.nydp +50 -39
  7. data/lib/lisp/core-020-utils.nydp +5 -5
  8. data/lib/lisp/core-030-syntax.nydp +103 -61
  9. data/lib/lisp/core-035-flow-control.nydp +18 -9
  10. data/lib/lisp/core-037-list-utils.nydp +36 -14
  11. data/lib/lisp/core-039-module.nydp +24 -0
  12. data/lib/lisp/core-040-utils.nydp +41 -23
  13. data/lib/lisp/core-041-string-utils.nydp +37 -9
  14. data/lib/lisp/core-042-date-utils.nydp +21 -1
  15. data/lib/lisp/core-043-list-utils.nydp +93 -67
  16. data/lib/lisp/core-045-dox-utils.nydp +5 -0
  17. data/lib/lisp/core-080-pretty-print.nydp +55 -17
  18. data/lib/lisp/core-090-hook.nydp +35 -1
  19. data/lib/lisp/core-100-utils.nydp +130 -28
  20. data/lib/lisp/core-110-hash-utils.nydp +61 -0
  21. data/lib/lisp/core-120-settings.nydp +46 -0
  22. data/lib/lisp/core-130-validations.nydp +51 -0
  23. data/lib/lisp/{core-060-benchmarking.nydp → core-900-benchmarking.nydp} +108 -5
  24. data/lib/lisp/tests/accum-examples.nydp +28 -1
  25. data/lib/lisp/tests/aif-examples.nydp +8 -3
  26. data/lib/lisp/tests/andify-examples.nydp +7 -0
  27. data/lib/lisp/tests/at-syntax-examples.nydp +17 -0
  28. data/lib/lisp/tests/best-examples.nydp +9 -0
  29. data/lib/lisp/tests/builtin-tests.nydp +19 -0
  30. data/lib/lisp/tests/case-examples.nydp +14 -0
  31. data/lib/lisp/tests/cdr-set-examples.nydp +6 -0
  32. data/lib/lisp/tests/date-examples.nydp +56 -1
  33. data/lib/lisp/tests/destructuring-examples.nydp +5 -5
  34. data/lib/lisp/tests/detect-examples.nydp +12 -0
  35. data/lib/lisp/tests/dp-examples.nydp +24 -0
  36. data/lib/lisp/tests/empty-examples.nydp +1 -1
  37. data/lib/lisp/tests/error-tests.nydp +4 -4
  38. data/lib/lisp/tests/filter-forms-examples.nydp +30 -0
  39. data/lib/lisp/tests/foundation-test.nydp +12 -0
  40. data/lib/lisp/tests/hash-examples.nydp +26 -2
  41. data/lib/lisp/tests/list-grep-examples.nydp +40 -0
  42. data/lib/lisp/tests/list-tests.nydp +58 -1
  43. data/lib/lisp/tests/map-hash-examples.nydp +11 -0
  44. data/lib/lisp/tests/mapreduce-examples.nydp +10 -0
  45. data/lib/lisp/tests/module-examples.nydp +10 -0
  46. data/lib/lisp/tests/multi-assign-examples.nydp +6 -0
  47. data/lib/lisp/tests/parser-tests.nydp +21 -0
  48. data/lib/lisp/tests/pretty-print-tests.nydp +16 -13
  49. data/lib/lisp/tests/set-difference-examples.nydp +8 -0
  50. data/lib/lisp/tests/set-intersection-examples.nydp +32 -0
  51. data/lib/lisp/tests/set-union-examples.nydp +24 -0
  52. data/lib/lisp/tests/settings-examples.nydp +40 -0
  53. data/lib/lisp/tests/sort-examples.nydp +8 -0
  54. data/lib/lisp/tests/string-tests.nydp +61 -1
  55. data/lib/lisp/tests/syntax-tests.nydp +5 -1
  56. data/lib/lisp/tests/to-integer-examples.nydp +16 -0
  57. data/lib/lisp/tests/validation-examples.nydp +15 -0
  58. data/lib/lisp/tests/zap-examples.nydp +12 -0
  59. data/lib/nydp.rb +13 -7
  60. data/lib/nydp/assignment.rb +10 -3
  61. data/lib/nydp/builtin.rb +1 -1
  62. data/lib/nydp/builtin/abs.rb +8 -0
  63. data/lib/nydp/builtin/cdr_set.rb +1 -6
  64. data/lib/nydp/builtin/date.rb +15 -1
  65. data/lib/nydp/builtin/error.rb +1 -1
  66. data/lib/nydp/builtin/handle_error.rb +1 -1
  67. data/lib/nydp/builtin/hash.rb +27 -45
  68. data/lib/nydp/builtin/inspect.rb +1 -1
  69. data/lib/nydp/builtin/plus.rb +10 -2
  70. data/lib/nydp/builtin/rand.rb +18 -0
  71. data/lib/nydp/builtin/random_string.rb +2 -2
  72. data/lib/nydp/builtin/ruby_wrap.rb +72 -0
  73. data/lib/nydp/builtin/set_intersection.rb +8 -0
  74. data/lib/nydp/builtin/set_union.rb +8 -0
  75. data/lib/nydp/builtin/string_match.rb +2 -2
  76. data/lib/nydp/builtin/string_pad_left.rb +7 -0
  77. data/lib/nydp/builtin/string_pad_right.rb +7 -0
  78. data/lib/nydp/builtin/string_replace.rb +1 -1
  79. data/lib/nydp/builtin/string_split.rb +1 -2
  80. data/lib/nydp/builtin/to_integer.rb +23 -0
  81. data/lib/nydp/builtin/to_string.rb +2 -9
  82. data/lib/nydp/builtin/type_of.rb +9 -6
  83. data/lib/nydp/closure.rb +0 -3
  84. data/lib/nydp/cond.rb +23 -1
  85. data/lib/nydp/context_symbol.rb +14 -6
  86. data/lib/nydp/core.rb +45 -33
  87. data/lib/nydp/core_ext.rb +54 -0
  88. data/lib/nydp/date.rb +37 -31
  89. data/lib/nydp/function_invocation.rb +34 -26
  90. data/lib/nydp/hash.rb +5 -6
  91. data/lib/nydp/helper.rb +41 -25
  92. data/lib/nydp/interpreted_function.rb +68 -40
  93. data/lib/nydp/literal.rb +1 -1
  94. data/lib/nydp/pair.rb +25 -9
  95. data/lib/nydp/parser.rb +8 -6
  96. data/lib/nydp/string_atom.rb +16 -22
  97. data/lib/nydp/symbol.rb +40 -27
  98. data/lib/nydp/symbol_lookup.rb +7 -7
  99. data/lib/nydp/tokeniser.rb +2 -2
  100. data/lib/nydp/truth.rb +17 -10
  101. data/lib/nydp/version.rb +1 -1
  102. data/lib/nydp/vm.rb +7 -2
  103. data/nydp.gemspec +2 -4
  104. data/spec/date_spec.rb +115 -22
  105. data/spec/embedded_spec.rb +12 -12
  106. data/spec/foreign_hash_spec.rb +14 -2
  107. data/spec/hash_non_hash_behaviour_spec.rb +7 -7
  108. data/spec/hash_spec.rb +24 -2
  109. data/spec/nydp_spec.rb +14 -2
  110. data/spec/parser_spec.rb +27 -16
  111. data/spec/rand_spec.rb +45 -0
  112. data/spec/spec_helper.rb +13 -1
  113. data/spec/symbol_spec.rb +31 -0
  114. data/spec/time_spec.rb +1 -1
  115. metadata +38 -37
  116. data/lib/nydp/builtin/car.rb +0 -7
  117. data/lib/nydp/builtin/cdr.rb +0 -7
  118. data/lib/nydp/builtin/cons.rb +0 -9
@@ -40,21 +40,30 @@
40
40
  (loop (assign ,v ,gi) (< ,v ,gm) (assign ,v (+ ,v 1))
41
41
  ,@body))))
42
42
 
43
- (mac curry (func . args1)
44
- ; return a new function which is the original function with
45
- ; the given args1 already applied
46
- ; arguments to the new function are whatever arguments remain
47
- ; for the old function
48
- `(fn args (apply ,func ,@args1 args)))
43
+ ;; return a new function which is the original function with
44
+ ;; the given args1 already applied
45
+ ;; arguments to the new function are whatever arguments remain
46
+ ;; for the old function
47
+ ;; Could be (mac curry things `(fn args (apply ,@things args))) but less readable
48
+ (mac curry (f . args0)
49
+ `(fn args
50
+ (apply ,f ,@args0 args)))
51
+
52
+ ;; like curry, but the returned function takes only a single arg (assumes all
53
+ ;; args but one are provided here)
54
+ ;; Could be (mac curry1 things `(fn (arg) (,@things arg))) but less readable
55
+ (mac curry1 (f . args)
56
+ `(fn (arg)
57
+ (,f ,@args arg)))
49
58
 
50
59
  (mac cache-get (hsh key val)
51
60
  ; if ,key is already in ,hsh - return the associated value.
52
61
  ; if ,key is not already in ,hsh - evaluate ,val, store the result
53
62
  ; under ,key in ,hsh, and return it
54
63
  (w/uniq (h k v)
55
- `(with (,h ,hsh ,k ,key)
56
- (let ,v (hash-get ,h ,k)
57
- (or ,v (returnlet ,v ,val (hash-set ,h ,k ,v)))))))
64
+ `(with (,h ,hsh ,k ,key)
65
+ (let ,v (hash-get ,h ,k)
66
+ (or ,v (returnlet ,v ,val (hash-set ,h ,k ,v)))))))
58
67
 
59
68
  (mac defmemo (name args . body)
60
69
  ; same as 'def, but caches the result, keyed on args, so for a given set of args the result
@@ -6,35 +6,57 @@
6
6
  (cons (map car args)
7
7
  (apply zip (map cdr args)))))
8
8
 
9
+ ; invokes 'f for each element of 'things, first element processed first
10
+ ; ( "l" in "eachl" = "leftmost first" )
11
+ (def eachl (f things)
12
+ (when things
13
+ (f (car things))
14
+ (eachl f (cdr things))))
15
+
16
+ ;; if things is a pair,
17
+ ;; if (cdr things) is nil, return (car things)
18
+ ;; else recurse on (cdr things)
19
+ ;; else return things
20
+ (def list/last (things)
21
+ (if (pair? things)
22
+ (aif (cdr things)
23
+ (list/last it)
24
+ (car things))
25
+ things))
26
+
27
+ ; invokes 'f for each element of 'things, last element processed first
28
+ ; ( "r" in "eachr" = "rightmost first" )
9
29
  (def eachr (f things)
10
30
  (when things
11
31
  (eachr f (cdr things))
12
32
  (f (car things))))
13
33
 
14
- (mac push (x things)
15
- ; assign (cons x things) to things
16
- `(= ,things (cons ,x ,things)))
34
+ ; assign (cons x things) to things
35
+ (mac push (x things) `(= ,things (cons ,x ,things)))
17
36
 
18
- (def flatten (things)
19
- ; flatten the given list, recursively
37
+ ; flatten the given list, transforming each leaf-item, recursively
38
+ (def flatmap (f things)
20
39
  (let acc nil
21
40
  (rfnwith flattenize (x things)
22
41
  (if (pair? x)
23
42
  (eachr flattenize x)
24
- (push x acc)))
43
+ x
44
+ (push (f x) acc)))
25
45
  acc))
26
46
 
47
+ ; flatten the given list, recursively
48
+ (def flatten (things) (flatmap x1 things))
49
+
50
+ ; given a list 'al of form '( (k0 v0) (k1 v1) (k2 v2) ... (kn vn) ) and
51
+ ; a 'key, returns the list (kx vx) from 'al where kx is equal to 'key
52
+ ; #attribution: inspiration from arc.arc
27
53
  (def assoc (key al)
28
- ; given a list 'al of form '( (k0 v0) (k1 v1) (k2 v2) ... (kn vn) ) and
29
- ; a 'key, returns the list (kx vx) from 'al where kx is equal to 'key
30
- ; #attribution: inspiration from arc.arc
31
54
  (if (pair? al)
32
55
  (if (caris key (car al))
33
56
  (car al)
34
57
  (assoc key (cdr al)))))
35
58
 
36
- (def alref (key al)
37
- ; given a list 'al of form '( (k0 v0) (k1 v1) (k2 v2) ... (kn vn) ) and
38
- ; a 'key, returns vx from 'al where kx is equal to 'key
39
- ; #attribution: lifted almost directly from arc.arc
40
- (cadr (assoc key al)))
59
+ ; given a list 'al of form '( (k0 v0) (k1 v1) (k2 v2) ... (kn vn) ) and
60
+ ; a 'key, returns vx from 'al where kx is equal to 'key
61
+ ; #attribution: lifted almost directly from arc.arc
62
+ (def alref (key al) (cadr (assoc key al)))
@@ -0,0 +1,24 @@
1
+ ;; creates a private namespace for support functions for one or more explicitly exported/public functions
2
+ ;;
3
+ ;; (module foo
4
+ ;; (def h ...)
5
+ ;; (def u ...)
6
+ ;; (export bar (x y) (h u x y)))
7
+ ;;
8
+ ;; results in 'foo/bar being universally available, but 'h and 'u are visible only within the module and override
9
+ ;; any other 'h or 'u defined elsewhere, in the scope of the module.
10
+ (mac module (module-name . forms)
11
+ (let private-names nil
12
+ (let module-forms
13
+ { def (fn (name args . body)
14
+ (push nil private-names)
15
+ (push name private-names)
16
+ `(assign ,name (fn ,args ,@body)))
17
+ export-def macs.def
18
+ export (fn (name args . body)
19
+ `(export-def
20
+ ,(sym (+ (to-string module-name) "/" (to-string name)))
21
+ ,args
22
+ ,@body)) }
23
+ (let module-body (pre-compile-each module-forms forms)
24
+ `(with (,@private-names) ,@module-body)))))
@@ -11,22 +11,20 @@
11
11
  (def num? (arg) (comment "true if arg is a number") (isa 'number arg))
12
12
  (def string? (arg) (comment "true if arg is a string") (isa 'string arg))
13
13
 
14
- (mac just (expr)
15
- ; this is useful sometimes when 'expr can't stand on its own due to lexical ambiguity, most often in string interpolations
16
- ; for example, in "hello ~person, how are you", the parser will try to interpolate the symbol "person," rather than the
17
- ; expected "person". In this case, use "hello ~(just person), how are you"
18
- expr)
14
+ ;; this is useful sometimes when 'expr can't stand on its own due to lexical ambiguity, most often in string interpolations
15
+ ;; for example, in "hello ~person, how are you", the parser will try to interpolate the symbol "person," rather than the
16
+ ;; expected "person". In this case, use "hello ~(just person), how are you"
17
+ (mac just (expr) expr)
19
18
 
20
19
  (def quotify (arg) `(quote ,arg))
21
20
 
22
- (defmemo k (arg)
23
- ; return a function that always returns 'arg, similar to K in SKI calculus
24
- (fn nil arg))
21
+ ;; return a function that always returns 'arg, similar to K in SKI calculus
22
+ (defmemo k (arg) (fn nil arg))
25
23
 
24
+ ;; return the length of 'things where 'things may be nil, a string, list or hash
25
+ ;; length of nil is zero, length of hash is number of keys, length of string
26
+ ;; is number of characters, length of list is number of direct items - no recursive counting
26
27
  (def len (things)
27
- ; return the length of 'things where 'things may be nil, a string, list or hash
28
- ; length of nil is zero, length of hash is number of keys, length of string
29
- ; is number of characters, length of list is number of direct items - no recursive counting
30
28
  (chapter list-manipulation)
31
29
  (chapter string-manipulation)
32
30
  (chapter hash-manipulation)
@@ -38,25 +36,34 @@
38
36
 
39
37
  (assign dynamics (hash))
40
38
 
41
- (mac dynamic (name)
42
- ; creates a dynamic variable.
43
- (hash-set dynamics name t)
44
- (let with-mac-name (sym "w/~name")
39
+ ;; creates a dynamic variable.
40
+ (mac dynamic (name initial)
41
+ (let with-mac-name (sym:+ "w/" name)
45
42
  (w/uniq prev
46
43
  `(do
47
- (mac ,with-mac-name (new-value . body)
48
- (w/uniq result
49
- `(let ,',prev (hash-get (thread-locals) ',',name)
50
- (hash-set (thread-locals) ',',name ,new-value)
51
- (let ,result (do ,@body)
52
- (hash-set (thread-locals) ',',name ,',prev)
53
- ,result))))
54
- (def ,name () (hash-get (thread-locals) ',name))))))
44
+ (hash-set dynamics ',name t)
45
+ (mac ,with-mac-name (new-value . body)
46
+ (w/uniq result
47
+ `(let ,',prev (hash-get (thread-locals) ',',name)
48
+ (hash-set (thread-locals) ',',name ,new-value)
49
+ (returning (do ,@body)
50
+ (hash-set (thread-locals) ',',name ,',prev)))))
51
+ ,(if initial `(hash-set (thread-locals) ',name ,initial))
52
+ (def ,name () (hash-get (thread-locals) ',name))))))
53
+
54
+
55
+ ;; overrides 'privately defined earlier in documentation manager
56
+ (dynamic privately)
57
+
58
+ ;; suppress documentation of anything defined in 'body
59
+ (mac in-private body
60
+ `(w/privately t ,@body))
55
61
 
56
62
  (mac mapx (things x expr)
57
63
  ; a macro wrapper for 'map
58
64
  ; 'things is a list, 'x is the name of a variable, and 'expr
59
65
  ; is evaluated and collected for each 'x in 'things
66
+ ; usage: (mapx items v (to-string v)) equivalent to (map to-string items)
60
67
  (chapter list-manipulation)
61
68
  `(map (fun (,x) ,expr) ,things))
62
69
 
@@ -83,3 +90,14 @@
83
90
  (chapter string-manipulation)
84
91
  (chapter hash-manipulation)
85
92
  (!empty? thing))
93
+
94
+ ;; returns the first non-empty item in 'args
95
+ ;; mac equivalent of (detect present? args)
96
+ (mac dp args
97
+ (if args
98
+ (w/uniq nearg
99
+ `(let ,nearg ,(car args)
100
+ (if (empty? ,nearg)
101
+ (dp ,@(cdr args))
102
+ ,nearg)))
103
+ nil))
@@ -1,7 +1,8 @@
1
1
  (chapter-start 'string-manipulation "utilities for manipulating strings")
2
2
 
3
+ ; return a new string with leading and trailing whitespace removed
3
4
  (def string-strip (txt)
4
- (string-replace "(^\\s+|\\s+$)" "" txt))
5
+ (string-replace "(\\A\\s+|\\s+\\z)" "" txt))
5
6
 
6
7
  (def joinstr (txt . things)
7
8
  ; flatten 'things into a single list (ie unnest lists)
@@ -15,14 +16,41 @@
15
16
  (flatten (map (fn (x) (list txt x))
16
17
  (cdr joinables))))))
17
18
 
18
- (def j items
19
- ; delegate to 'joinstr with an empty join string
20
- ; shortcut for (joinstr "" items)
21
- (joinstr "" items))
19
+ ; stringify join all the things and join them with no separator, like (joinstr "" things)
20
+ (def j things
21
+ (apply + (flatmap to-string things)))
22
22
 
23
+ ; string-interpolation syntax emits this form. Default implementation
24
+ ; is to delegate to 'j , but containing forms may use macros that
25
+ ; override this in order to provide specific interpolation behaviour
26
+ ; (for example, formatting numbers or stripping HTML tags)
23
27
  (def string-pieces pieces
24
- ; string-interpolation syntax emits this form. Default implementation
25
- ; is to delegate to 'j , but containing forms may use macros that
26
- ; override this in order to provide specific interpolation behaviour
27
- ; (for example, formatting numbers or stripping HTML tags)
28
28
  (j pieces))
29
+
30
+ ; return the first 'length chars of string 'str
31
+ (def string-truncate (str length)
32
+ (string-replace "(.{~|length|}).*" "\\1" str))
33
+
34
+ ;; returns a function with args 'args whose body is 'str. 'str should be a string,
35
+ ;; 'args should correspond to interpolations within 'str
36
+ ;;
37
+ ;; example: (string-eval-fn "hello \~u.firstname" 'u)
38
+ ;; returns (fn (u) (string-pieces "hello " u.firstname))
39
+ (defmemo string-eval-fn (str args)
40
+ (eval `(fn ,args
41
+ ,(parse-in-string str))))
42
+
43
+ ;; assigns 'args respectively to 'arg-names and evals 'str in that context.
44
+ ;; Assumes 'str contains interpolations which reference 'arg-names.
45
+ ;; Useful for evaluating user-supplied strings ; dangerous for the same reason.
46
+ ;;
47
+ ;; example: (string/eval-with-args "\~x + \~y is \~(+ x y)" '(x y) 2 3)
48
+ ;; returns "2 + 3 is 5"
49
+ ;;
50
+ (def string/eval-with-args (str arg-names . args)
51
+ (on-err
52
+ (error (j "error evaluating " (inspect str)
53
+ "\nwith arg names " (inspect arg-names)
54
+ "\nand args " (inspect args)))
55
+ (apply (string-eval-fn str arg-names)
56
+ args)))
@@ -1,5 +1,25 @@
1
1
  (chapter-start 'date-time "utilities for retrieving and manipulating dates and times")
2
2
 
3
+ ;; return a date for the current day
3
4
  (def today ()
4
- ; return a date for the current day
5
5
  (date))
6
+
7
+ ;; return a Time object representing the time 's seconds ago
8
+ (def seconds-ago (s)
9
+ (- (time) s))
10
+
11
+ (def anniversary/previous (anchor anniv)
12
+ (let d (date anchor.year
13
+ anniv.month
14
+ anniv.day)
15
+ (if (< d anchor)
16
+ d
17
+ d.last-year)))
18
+
19
+ (def anniversary/next (anchor anniv)
20
+ (let d (date anchor.year
21
+ anniv.month
22
+ anniv.day)
23
+ (if (> d anchor)
24
+ d
25
+ d.next-year)))
@@ -23,76 +23,71 @@
23
23
  (intersperse inbetween (cdr things)))
24
24
  things))
25
25
 
26
+ ;; expects 'things a list of lists, joins the lists
27
+ ;; returns (a b X c d X e f)
28
+ ;; placing 'inbetween in between each list.
29
+ ;; For example (intersperse-splicing 'X '((a b) (c d) (e f)))
26
30
  (def intersperse-splicing (inbetween things)
27
- ; expects 'things a list of lists, joins the lists
28
- ; placing 'inbetween in between each list.
29
- ; For example (intersperse-splicing 'X '((a b) (c d) (e f)))
30
- ; returns (a b X c d X e f)
31
31
  (apply joinlists (intersperse (list inbetween) things)))
32
32
 
33
- (def collect (f things)
34
- ; if 'things is a list, return all the items in the list for which 'f returns non-nil
35
- ; otherwise, return 'things if (f things) is non-nil
36
- ; otherwise, nil
37
- ; note that this preserves improper lists and may return only the lastcdr if all else fails...
38
- (rfnwith collector (items things)
39
- (if (no items)
40
- nil
41
- (pair? items)
42
- (if (f (car items))
43
- (cons (car items)
44
- (collector (cdr items)))
45
- (collector (cdr items)))
46
- (f items)
47
- items)))
33
+ ;; if 'things is a list, return all the items in the list for which 'f returns non-nil
34
+ ;; otherwise, return 'things if (f things) is non-nil
35
+ ;; otherwise, nil
36
+ ;; note that this preserves improper lists and may return only the lastcdr if all else fails...
37
+ (def collect (f items)
38
+ (if (pair? items)
39
+ (if (f (car items))
40
+ (cons (car items)
41
+ (collect f (cdr items)))
42
+ (collect f (cdr items)))
43
+ items
44
+ (if (f items)
45
+ items)))
48
46
 
49
47
  (assign select collect)
50
48
 
51
- (def compact (things)
52
- ; return a new list containing only non-nil items from the given list
53
- (collect present? things))
49
+ ;; return a new list containing only 'present? items from the given list
50
+ (def compact (things) (collect present? things))
54
51
 
52
+ ;; return the sum of all non-nil values (consider nil as zero)
55
53
  (def +nz args
56
- ; return the sum of all non-nil values (consider nil as zero)
57
54
  (apply + (compact args)))
58
55
 
56
+ ;; return all the items in 'things for which 'f returns nil
59
57
  (def reject (f things)
60
- ; return all the items in 'things for which 'f returns nil
61
58
  (collect !f things))
62
59
 
60
+ ;; returns the n-th item in the list 'things
63
61
  (def nth (n things)
64
- ; returns the n-th item in the list 'things
65
62
  (if (eq? n 0)
66
63
  (car things)
67
64
  (nth (- n 1) (cdr things))))
68
65
 
66
+ ;; repeatedly assigns an element of 'things to 'var,
67
+ ;; and executes 'body each time
69
68
  (mac each (var things . body)
70
- ; repeatedly assigns an element of 'things to 'var,
71
- ; and executes 'body each time
72
69
  (w/uniq (xs c)
73
- `((rfn ,c (,xs)
74
- (if (pair? ,xs)
75
- (do (let ,var (car ,xs) ,@body)
76
- (,c (cdr ,xs)))))
77
- ,things)))
70
+ `(rfnwith ,c (,xs ,things)
71
+ (if (pair? ,xs)
72
+ (do (let ,var (car ,xs) ,@body)
73
+ (,c (cdr ,xs)))))))
78
74
 
79
75
  (def reduce (f things)
80
- ((rfn rd (acc list)
81
- (if (pair? list)
82
- (rd (f acc (car list))
83
- (cdr list))
84
- acc))
85
- (car things) (cdr things)))
86
-
76
+ (rfnwith rd (acc (car things) list (cdr things))
77
+ (if (pair? list)
78
+ (rd (f acc (car list))
79
+ (cdr list))
80
+ acc)))
81
+
82
+ ;; t if this is a proper list (last cdr is nil)
83
+ ;; nil otherwise (last cdr is neither cons nor nil)
87
84
  (def proper? (list)
88
- ; t if this is a proper list (last cdr is nil)
89
- ; nil otherwise (last cdr is neither cons nor nil)
90
85
  (or (no list)
91
86
  (and (pair? list)
92
87
  (proper? (cdr list)))))
93
88
 
89
+ ;; returns the first 'n items in the list 'things
94
90
  (def firstn (n things)
95
- ; returns the first 'n items in the list 'things
96
91
  (if (eq? n 0) nil
97
92
  things (cons (car things)
98
93
  (firstn (- n 1)
@@ -140,34 +135,39 @@
140
135
  (detect (curry eq? f)
141
136
  things)))
142
137
 
138
+ ;; split things into a list of lists each n long
143
139
  (def tuples (n things)
144
- ;; split things into a list of lists each n long
145
140
  (rfnwith _ (list things)
146
141
  (if (no list)
147
142
  nil
148
143
  (cons (firstn n list) (_ (nthcdr n list))))))
149
144
 
150
- (def range (start stop)
151
- ; return a list containing the range
152
- ; of elements starting with 'start, up
153
- ; to but not including 'stop
154
- (if (< start stop)
155
- (cons start
156
- (range (+ start 1)
157
- stop))))
158
-
159
- (def best (f things)
160
- (if (no things)
161
- nil
162
- (let winner (car things)
163
- (each thing (cdr things)
164
- (if (f thing winner)
165
- (= winner thing)))
166
- winner)))
145
+ ;; iterates through 'things pairwise (a,b then b,c then c,d etc), returns whichever is preferred by 'better-p
146
+ ;; example (best > '(3 1 4 1 5 9 2)) returns 9
147
+ ;; "better-p" -> takes two args a and b, return t if a is "better" than b, nil otherwise
148
+ (def best (better-p things)
149
+ (reduce (fn (a b)
150
+ (if (better-p a b) a b))
151
+ things))
167
152
 
168
153
  (def min things (best < things))
169
154
  (def max things (best > things))
170
155
 
156
+ ;; returns a function taking two args a and b, that compares attributes of two objects
157
+ ;; 'map-f takes one arg a returning a1
158
+ ;; 'compare-p takes two args a1 and b1, returns t if a1 is "better" than b1
159
+ ;;
160
+ ;; useful in conjunction with 'best : (best (map-compare-f > &size) (list { size 1 } { size 7 } { size 3 })) returns { size 7 }
161
+ (def map-compare-f (compare-p map-f)
162
+ (fn (a b) (compare-p (map-f a) (map-f b))))
163
+
164
+ ;; iterate over 'things, calling 'on-atom for each non-list element, and calling
165
+ ;; 'on-list for each list element.
166
+ ;; 'on-atom takes one parameter, the element in question;
167
+ ;; 'on-list takes two parameters: a function to call for recursing, and the list in question.
168
+ ;; 'on-list should be something like (fn (rec xs) (foo xs) (map rec xs)) to construct a new list,
169
+ ;; or (fn (rec xs) (foo xs) (eachl rec xs)) to iterate without constructing a new list
170
+ ;; see 'list-gsub for an example of constructing a new list using this.
171
171
  (def map-recurse (on-atom on-list things)
172
172
  ((afn (xs)
173
173
  (if (pair? xs)
@@ -176,8 +176,20 @@
176
176
  (on-atom xs)))
177
177
  things))
178
178
 
179
+ ;; like map-recurse, but doesn't depend on caller to initiate recursion
180
+ ;; 'on-atom and 'on-list are functions each taking one parameter
181
+ ;; return value is last returned item from on-atom or on-list
182
+ (def list/traverse (on-atom on-list things)
183
+ (map-recurse
184
+ (fn (s)
185
+ (on-atom s))
186
+ (fn (rec xs)
187
+ (on-list xs)
188
+ (eachl rec xs))
189
+ things))
190
+
191
+ ;; recursively replaces 'old with 'new inside 'list
179
192
  (def list-gsub (list old new)
180
- ; recursively replaces 'old with 'new inside 'list
181
193
  (map-recurse (fn (s) (if (eq? s old) new s))
182
194
  (fn (m things)
183
195
  (if (eq? things old)
@@ -186,8 +198,8 @@
186
198
  list))
187
199
 
188
200
  (def all? (f things)
189
- ; if 'things is a list, true when all items are non-nil
190
- ; if 'things is an atom, true when non-nil
201
+ ; if 'things is a list, true when f(thing) is non-nil for each thing in things
202
+ ; if 'things is an atom, true when f(things) is non-nil
191
203
  (if (pair? things)
192
204
  (and (f:car things)
193
205
  (or (no:cdr things)
@@ -195,8 +207,8 @@
195
207
  (f things)))
196
208
 
197
209
  (def any? (f things)
198
- ; if 'things is a list, true when at least one item is non-nil
199
- ; if 'things is an atom, true when non-nil
210
+ ; if 'things is a list, true when f(thing) is non-nil for at least one thing in things
211
+ ; if 'things is an atom, true when f(thing) is non-nil
200
212
  (if (pair? things)
201
213
  (or (f:car things)
202
214
  (and (cdr things)
@@ -204,8 +216,8 @@
204
216
  (f things)))
205
217
 
206
218
  (def none? (f things)
207
- ; if 'things is a list, true when all items are nil
208
- ; if 'things is an atom, true when nil
219
+ ; if 'things is a list, true when f(thing) is nil for each thing in things
220
+ ; if 'things is an atom, true when f(things) is nil
209
221
  (if (pair? things)
210
222
  (and (no:f:car things)
211
223
  (none? f (cdr things)))
@@ -221,3 +233,17 @@
221
233
  matchers
222
234
  (matchers things)
223
235
  t))
236
+
237
+ ; given an arg 'f, invoke 'f with no args
238
+ (def self-invoke (f) (f))
239
+
240
+ ;; returns the first element of 'things iff it is the only element of 'things
241
+ (def list-single-element (things)
242
+ (if (no (cdr things)) (car things)))
243
+
244
+ ;; like map, but function 'f takes two arguments: the thing and the 0-based index of the thing
245
+ (def map-with-index (f things)
246
+ (let i -1
247
+ (map (fn (thing)
248
+ (f thing (++ i)))
249
+ things)))