1 // This file is distributed under the BSD License.
2 // See "license.txt" for details.
3 // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
4 // and 2009-2017, Jason Turner (jason@emptycrate.com)
5 // http://www.chaiscript.com
6 
7 #ifndef CHAISCRIPT_PRELUDE_HPP_
8 #define CHAISCRIPT_PRELUDE_HPP_
9 
10 namespace chaiscript {
11 struct ChaiScript_Prelude {
chaiscript_preludechaiscript::ChaiScript_Prelude12   static std::string chaiscript_prelude() {  return R"chaiscript(
13 
14 def lt(l, r) {
15   if (call_exists(`<`, l, r)) {
16     l < r
17   } else {
18     type_name(l) < type_name(r)
19   }
20 }
21 
22 
23 def gt(l, r) {
24   if (call_exists(`>`, l, r)) {
25     l > r
26   } else {
27     type_name(l) > type_name(r)
28   }
29 }
30 
31 def eq(l, r) {
32   if (call_exists(`==`, l, r)) {
33     l == r
34   } else {
35     false
36   }
37 }
38 
39 def new(x) {
40   eval(type_name(x))();
41 }
42 
43 def clone(double x) {
44   double(x).clone_var_attrs(x)
45 }
46 
47 def clone(string x) {
48   string(x).clone_var_attrs(x)
49 }
50 
51 def clone(vector x) {
52   vector(x).clone_var_attrs(x)
53 }
54 
55 
56 def clone(int x) {
57   int(x).clone_var_attrs(x)
58 }
59 
60 def clone(x) : function_exists(type_name(x)) && call_exists(eval(type_name(x)), x)
61 {
62   eval(type_name(x))(x).clone_var_attrs(x);
63 }
64 
65 
66 # to_string for Pair()
67 def to_string(x) : call_exists(first, x) && call_exists(second, x) {
68   "<" + x.first.to_string() + ", " + x.second.to_string() + ">";
69 }
70 
71 # to_string for containers
72 def to_string(x) : call_exists(range, x) && !x.is_type("string"){
73   "[" + x.join(", ") + "]";
74 }
75 
76 # Prints to console with no carriage return
77 def puts(x) {
78   print_string(x.to_string());
79 }
80 
81 # Prints to console with carriage return
82 def print(x) {
83   println_string(x.to_string());
84 }
85 
86 # Returns the maximum value of two numbers
87 def max(a, b) {
88   if (a>b) {
89     a
90   } else {
91     b
92   }
93 }
94 
95 # Returns the minimum value of two numbers
96 def min(a, b)
97 {
98   if (a<b)
99   {
100     a
101   } else {
102     b
103   }
104 }
105 
106 
107 # Returns true if the value is odd
108 def odd(x)  {
109   if (x % 2 == 1)
110   {
111     true
112   } else {
113     false
114   }
115 }
116 
117 
118 # Returns true if the value is even
119 def even(x)
120 {
121   if (x % 2 == 0)
122   {
123     true
124   } else {
125     false
126   }
127 }
128 
129 
130 # Inserts the third value at the position of the second value into the container of the first
131 # while making a clone.
132 def insert_at(container, pos, x)
133 {
134   container.insert_ref_at(pos, clone(x));
135 }
136 
137 # Returns the reverse of the given container
138 def reverse(container) {
139   auto retval := new(container);
140   auto r := range(container);
141   while (!r.empty()) {
142     retval.push_back(r.back());
143     r.pop_back();
144   }
145   retval;
146 }
147 
148 
149 def range(r) : call_exists(range_internal, r)
150 {
151   var ri := range_internal(r);
152   ri.get_var_attr("internal_obj") := r;
153   ri;
154 }
155 
156 # Return a range from a range
157 def range(r) : call_exists(empty, r) && call_exists(pop_front, r) && call_exists(pop_back, r) && call_exists(back, r) && call_exists(front, r)
158 {
159   clone(r);
160 }
161 
162 
163 # The retro attribute that contains the underlying range
164 attr retro::m_range;
165 
166 # Creates a retro from a retro by returning the original range
167 def retro(r) : call_exists(get_type_name, r) && get_type_name(r) == "retro"
168 {
169   clone(r.m_range)
170 }
171 
172 
173 # Creates a retro range from a range
174 def retro::retro(r) : call_exists(empty, r) && call_exists(pop_front, r) && call_exists(pop_back, r) && call_exists(back, r) && call_exists(front, r)
175 {
176   this.m_range = r;
177 }
178 
179 # Returns the first value of a retro
180 def retro::front()
181 {
182   back(this.m_range)
183 }
184 
185 # Returns the last value of a retro
186 def retro::back()
187 {
188   front(this.m_range)
189 }
190 
191 # Moves the back iterator of a retro towards the front by one
192 def retro::pop_back()
193 {
194   pop_front(this.m_range)
195 }
196 
197 # Moves the front iterator of a retro towards the back by one
198 def retro::pop_front()
199 {
200   pop_back(this.m_range)
201 }
202 
203 # returns true if the retro is out of elements
204 def retro::empty()
205 {
206   empty(this.m_range);
207 }
208 
209 # Performs the second value function over the container first value
210 def for_each(container, func) : call_exists(range, container) {
211   var t_range := range(container);
212   while (!t_range.empty()) {
213     func(t_range.front());
214     t_range.pop_front();
215   }
216 }
217 
218 def any_of(container, func) : call_exists(range, container) {
219   var t_range := range(container);
220   while (!t_range.empty()) {
221     if (func(t_range.front())) {
222       return true;
223     }
224     t_range.pop_front();
225   }
226   false;
227 }
228 
229 def all_of(container, func) : call_exists(range, container) {
230   var t_range := range(container);
231   while (!t_range.empty()) {
232     if (!func(t_range.front())) {
233       return false;
234     }
235     t_range.pop_front();
236   }
237 
238   true;
239 }
240 
241 def back_inserter(container) {
242   bind(push_back, container, _);
243 }
244 
245 def contains(container, item, compare_func) : call_exists(range, container) {
246   auto t_range := range(container);
247   while (!t_range.empty()) {
248     if ( compare_func(t_range.front(), item) ) {
249       return true;
250     }
251 
252     t_range.pop_front();
253   }
254   false;
255 }
256 
257 def contains(container, item) {
258   contains(container, item, eq)
259 }
260 
261 def map(container, func, inserter) : call_exists(range, container) {
262   auto range := range(container);
263   while (!range.empty()) {
264     inserter(func(range.front()));
265     range.pop_front();
266   }
267 }
268 
269 # Performs the second value function over the container first value. Creates a new container with the results
270 def map(container, func) {
271   auto retval := new(container);
272   map(container, func, back_inserter(retval));
273   retval;
274 }
275 
276 # Performs the second value function over the container first value. Starts with initial and continues with each element.
277 def foldl(container, func, initial) : call_exists(range, container){
278   auto retval = initial;
279   auto range := range(container);
280   while (!range.empty()) {
281     retval = (func(range.front(), retval));
282     range.pop_front();
283   }
284   retval;
285 }
286 
287 # Returns the sum of the elements of the given value
288 def sum(container) {
289   foldl(container, `+`, 0.0)
290 }
291 
292 # Returns the product of the elements of the given value
293 def product(container) {
294   foldl(container, `*`, 1.0)
295 }
296 
297 # Returns a new container with the elements of the first value concatenated with the elements of the second value
298 def concat(x, y) : call_exists(clone, x) {
299   auto retval = x;
300   auto inserter := back_inserter(retval);
301   auto range := range(y);
302   while (!range.empty()) {
303     inserter(range.front());
304     range.pop_front();
305   }
306   retval;
307 }
308 
309 
310 def take(container, num, inserter) : call_exists(range, container) {
311   auto r := range(container);
312   auto i = num;
313   while ((i > 0) && (!r.empty())) {
314     inserter(r.front());
315     r.pop_front();
316     --i;
317   }
318 }
319 
320 
321 # Returns a new container with the given number of elements taken from the container
322 def take(container, num) {
323   auto retval := new(container);
324   take(container, num, back_inserter(retval));
325   retval;
326 }
327 
328 
329 def take_while(container, f, inserter) : call_exists(range, container) {
330   auto r := range(container);
331   while ((!r.empty()) && f(r.front())) {
332     inserter(r.front());
333     r.pop_front();
334   }
335 }
336 
337 
338 # Returns a new container with the given elements match the second value function
339 def take_while(container, f) {
340   auto retval := new(container);
341   take_while(container, f, back_inserter(retval));
342   retval;
343 }
344 
345 
346 def drop(container, num, inserter) : call_exists(range, container) {
347   auto r := range(container);
348   auto i = num;
349   while ((i > 0) && (!r.empty())) {
350     r.pop_front();
351     --i;
352   }
353   while (!r.empty()) {
354     inserter(r.front());
355     r.pop_front();
356   }
357 }
358 
359 
360 # Returns a new container with the given number of elements dropped from the given container
361 def drop(container, num) {
362   auto retval := new(container);
363   drop(container, num, back_inserter(retval));
364   retval;
365 }
366 
367 
368 def drop_while(container, f, inserter) : call_exists(range, container) {
369   auto r := range(container);
370   while ((!r.empty())&& f(r.front())) {
371     r.pop_front();
372   }
373   while (!r.empty()) {
374     inserter(r.front());
375     r.pop_front();
376   }
377 }
378 
379 
380 # Returns a new container with the given elements dropped that match the second value function
381 def drop_while(container, f) {
382   auto retval := new(container);
383   drop_while(container, f, back_inserter(retval));
384   retval;
385 }
386 
387 
388 # Applies the second value function to the container. Starts with the first two elements. Expects at least 2 elements.
389 def reduce(container, func) : container.size() >= 2 && call_exists(range, container) {
390   auto r := range(container);
391   auto retval = r.front();
392   r.pop_front();
393   retval = func(retval, r.front());
394   r.pop_front();
395   while (!r.empty()) {
396     retval = func(retval, r.front());
397     r.pop_front();
398   }
399   retval;
400 }
401 
402 
403 # Returns a string of the elements in container delimited by the second value string
404 def join(container, delim) {
405   auto retval = "";
406   auto range := range(container);
407   if (!range.empty()) {
408     retval += to_string(range.front());
409     range.pop_front();
410     while (!range.empty()) {
411       retval += delim;
412       retval += to_string(range.front());
413       range.pop_front();
414     }
415   }
416   retval;
417 }
418 
419 
420 def filter(container, f, inserter) : call_exists(range, container) {
421   auto r := range(container);
422   while (!r.empty()) {
423     if (f(r.front())) {
424       inserter(r.front());
425     }
426     r.pop_front();
427   }
428 }
429 
430 
431 # Returns a new Vector which match the second value function
432 def filter(container, f) {
433   auto retval := new(container);
434   filter(container, f, back_inserter(retval));
435   retval;
436 }
437 
438 
439 def generate_range(x, y, inserter) {
440   auto i = x;
441   while (i <= y) {
442     inserter(i);
443     ++i;
444   }
445 }
446 
447 
448 # Returns a new Vector which represents the range from the first value to the second value
449 def generate_range(x, y) {
450   auto retval := Vector();
451   generate_range(x,y,back_inserter(retval));
452   retval;
453 }
454 
455 
456 # Returns a new Vector with the first value to the second value as its elements
457 def collate(x, y) {
458   return [x, y];
459 }
460 
461 
462 def zip_with(f, x, y, inserter) : call_exists(range, x) && call_exists(range, y) {
463   auto r_x := range(x);
464   auto r_y := range(y);
465   while (!r_x.empty() && !r_y.empty()) {
466     inserter(f(r_x.front(), r_y.front()));
467     r_x.pop_front();
468     r_y.pop_front();
469   }
470 }
471 
472 
473 # Returns a new Vector which joins matching elements of the second and third value with the first value function
474 def zip_with(f, x, y) {
475   auto retval := Vector();
476   zip_with(f,x,y,back_inserter(retval));
477   retval;
478 }
479 
480 
481 # Returns a new Vector which joins matching elements of the first and second
482 def zip(x, y) {
483   zip_with(collate, x, y);
484 }
485 
486 
487 # Returns the position of the second value string in the first value string
488 def string::find(string substr) {
489   find(this, substr, size_t(0));
490 }
491 
492 
493 # Returns the position of last match of the second value string in the first value string
494 def string::rfind(string substr) {
495   rfind(this, substr, size_t(-1));
496 }
497 
498 
499 # Returns the position of the first match of elements in the second value string in the first value string
500 def string::find_first_of(string list) {
501   find_first_of(this, list, size_t(0));
502 }
503 
504 
505 # Returns the position of the last match of elements in the second value string in the first value string
506 def string::find_last_of(string list) {
507   find_last_of(this, list, size_t(-1));
508 }
509 
510 
511 # Returns the position of the first non-matching element in the second value string in the first value string
512 def string::find_first_not_of(string list) {
513   find_first_not_of(this, list, size_t(0));
514 }
515 
516 
517 # Returns the position of the last non-matching element in the second value string in the first value string
518 def string::find_last_not_of(string list) {
519   find_last_not_of(this, list, size_t(-1));
520 }
521 
522 
523 def string::ltrim() {
524   drop_while(this, fun(x) { x == ' ' || x == '\t' || x == '\r' || x == '\n'});
525 }
526 
527 
528 def string::rtrim() {
529   reverse(drop_while(reverse(this), fun(x) { x == ' ' || x == '\t' || x == '\r' || x == '\n'}));
530 }
531 
532 
533 def string::trim() {
534   ltrim(rtrim(this));
535 }
536 
537 
538 def find(container, value, Function compare_func) : call_exists(range, container) {
539   auto range := range(container);
540   while (!range.empty()) {
541     if (compare_func(range.front(), value)) {
542       return range;
543     } else {
544       range.pop_front();
545     }
546   }
547   range;
548 }
549 
550 
551 def find(container, value) {
552   find(container, value, eq)
553 }
554 
555 
556 )chaiscript";
557 }
558 
559 };
560 }
561 
562 #endif /* CHAISCRIPT_PRELUDE_HPP_ */
563