1 // Copyright 2016-2021 Doug Moen
2 // Licensed under the Apache License, version 2.0
3 // See accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0
4 
5 #include <libcurv/builtin.h>
6 
7 #include <libcurv/analyser.h>
8 #include <libcurv/bool.h>
9 #include <libcurv/die.h>
10 #include <libcurv/dir_record.h>
11 #include <libcurv/exception.h>
12 #include <libcurv/function.h>
13 #include <libcurv/import.h>
14 #include <libcurv/tree.h>
15 #include <libcurv/num.h>
16 #include <libcurv/pattern.h>
17 #include <libcurv/picker.h>
18 #include <libcurv/prim_expr.h>
19 #include <libcurv/program.h>
20 #include <libcurv/sc_compiler.h>
21 #include <libcurv/sc_context.h>
22 #include <libcurv/source.h>
23 #include <libcurv/system.h>
24 
25 #include <boost/math/constants/constants.hpp>
26 #include <boost/filesystem.hpp>
27 
28 #include <cassert>
29 #include <climits>
30 #include <cmath>
31 #include <cstdlib>
32 #include <string>
33 
34 using namespace std;
35 using namespace boost::math::double_constants;
36 
37 namespace curv {
38 
39 Shared<Meaning>
to_meaning(const Identifier & id) const40 Builtin_Value::to_meaning(const Identifier& id) const
41 {
42     return make<Constant>(share(id), value_);
43 }
44 
45 //----------------------------------------------//
46 // Templates for constructing builtin functions //
47 //----------------------------------------------//
48 
49 template <class Prim>
50 struct Unary_Array_Func : public Function
51 {
52     using Function::Function;
53     using Op = Unary_Array_Op<Prim>;
callcurv::Unary_Array_Func54     Value call(Value arg, Fail fl, Frame& fm) const override
55     {
56         return Op::call(fl, At_Arg(*this, fm), arg);
57     }
sc_call_exprcurv::Unary_Array_Func58     SC_Value sc_call_expr(Operation& argx, Shared<const Phrase> ph, SC_Frame& fm)
59     const override
60     {
61         return Op::sc_op(At_SC_Arg_Expr(*this, ph, fm), argx, fm);
62     }
63 };
64 
65 template <class Prim>
66 struct Binary_Array_Func : public Tuple_Function
67 {
Binary_Array_Funccurv::Binary_Array_Func68     Binary_Array_Func(const char* nm) : Tuple_Function(2,nm) {}
69     using Op = Binary_Array_Op<Prim>;
tuple_callcurv::Binary_Array_Func70     Value tuple_call(Fail fl, Frame& args) const override
71     {
72         return Op::call(fl, At_Arg(*this, args), args[0], args[1]);
73     }
sc_call_exprcurv::Binary_Array_Func74     SC_Value sc_call_expr(Operation& argx, Shared<const Phrase> ph, SC_Frame& fm)
75     const override
76     {
77         return Op::sc_op(At_SC_Arg_Expr(*this, ph, fm), argx, fm);
78     }
79 };
80 
81 template <class Prim>
82 struct Monoid_Func final : public Function
83 {
84     using Function::Function;
85     using Op = Binary_Array_Op<Prim>;
callcurv::Monoid_Func86     Value call(Value arg, Fail fl, Frame& fm) const override
87     {
88         return Op::reduce(fl, At_Arg(*this, fm), Prim::zero(), arg);
89     }
sc_call_exprcurv::Monoid_Func90     SC_Value sc_call_expr(Operation& argx, Shared<const Phrase> ph, SC_Frame& fm)
91     const override
92     {
93         return Op::sc_reduce(At_SC_Arg_Expr(*this, ph, fm),
94             Prim::zero(), argx, fm);
95     }
96 };
97 
98 //-------------------//
99 // Builtin Functions //
100 //-------------------//
101 
102 struct Is_Bool_Function : public Function
103 {
104     using Function::Function;
callcurv::Is_Bool_Function105     Value call(Value arg, Fail, Frame&) const override
106     {
107         return {is_bool(arg)};
108     }
109 };
110 struct Is_Char_Function : public Function
111 {
112     using Function::Function;
callcurv::Is_Char_Function113     Value call(Value arg, Fail, Frame&) const override
114     {
115         return {arg.is_char()};
116     }
117 };
118 struct Is_Symbol_Function : public Function
119 {
120     using Function::Function;
callcurv::Is_Symbol_Function121     Value call(Value arg, Fail, Frame&) const override
122     {
123         return {is_symbol(arg)};
124     }
125 };
126 struct Is_Num_Function : public Function
127 {
128     using Function::Function;
callcurv::Is_Num_Function129     Value call(Value arg, Fail, Frame&) const override
130     {
131         return {is_num(arg)};
132     }
133 };
134 struct Is_String_Function : public Function
135 {
136     using Function::Function;
callcurv::Is_String_Function137     Value call(Value arg, Fail, Frame&) const override
138     {
139         return {is_string(arg)};
140     }
141 };
142 struct Is_List_Function : public Function
143 {
144     using Function::Function;
callcurv::Is_List_Function145     Value call(Value arg, Fail, Frame& fm) const override
146     {
147         Generic_List glist(arg, Fail::soft, At_Frame(fm));
148         return {glist.is_list()};
149     }
150 };
151 struct Is_Record_Function : public Function
152 {
153     using Function::Function;
callcurv::Is_Record_Function154     Value call(Value arg, Fail, Frame&) const override
155     {
156         return {arg.maybe<Record>() != nullptr};
157     }
158 };
159 struct Is_Primitive_Func_Function : public Function
160 {
161     using Function::Function;
callcurv::Is_Primitive_Func_Function162     Value call(Value arg, Fail, Frame&) const override
163     {
164         return {arg.maybe<Function>() != nullptr};
165     }
166 };
167 struct Is_Func_Function : public Function
168 {
169     using Function::Function;
callcurv::Is_Func_Function170     Value call(Value arg, Fail, Frame& fm) const override
171     {
172         return {maybe_function(arg, At_Arg(*this, fm)) != nullptr};
173     }
174 };
175 
176 struct Bit_Prim : public Unary_Bool_To_Num_Prim
177 {
namecurv::Bit_Prim178     static const char* name() { return "bit"; }
callcurv::Bit_Prim179     static Value call(bool b, const Context&) { return {double(b)}; }
sc_callcurv::Bit_Prim180     static SC_Value sc_call(SC_Frame& fm, SC_Value arg)
181     {
182         auto result = fm.sc_.newvalue(SC_Type::Num(arg.type.count()));
183         fm.sc_.out() << "  " << result.type << " " << result << " = "
184             << result.type << "(" << arg << ");\n";
185         return result;
186     }
187 };
188 using Bit_Function = Unary_Array_Func<Bit_Prim>;
189 
190 #define UNARY_NUMERIC_FUNCTION(Func_Name,curv_name,c_name,glsl_name) \
191 struct Func_Name##Prim : public Unary_Num_SCVec_Prim \
192 { \
193     static const char* name() { return #curv_name; } \
194     static Value call(double x, const Context&) { return {c_name(x)}; } \
195     static SC_Value sc_call(SC_Frame& fm, SC_Value arg) \
196         { return sc_unary_call(fm, arg.type, #glsl_name, arg); } \
197 }; \
198 using Func_Name = Unary_Array_Func<Func_Name##Prim>; \
199 
UNARY_NUMERIC_FUNCTION(Sqrt_Function,sqrt,sqrt,sqrt)200 UNARY_NUMERIC_FUNCTION(Sqrt_Function, sqrt, sqrt, sqrt)
201 UNARY_NUMERIC_FUNCTION(Log_Function, log, log, log)
202 UNARY_NUMERIC_FUNCTION(Abs_Function, abs, abs, abs)
203 UNARY_NUMERIC_FUNCTION(Floor_Function, floor, floor, floor)
204 UNARY_NUMERIC_FUNCTION(Ceil_Function, ceil, ceil, ceil)
205 UNARY_NUMERIC_FUNCTION(Trunc_Function, trunc, trunc, trunc)
206 UNARY_NUMERIC_FUNCTION(Round_Function, round, rint, roundEven)
207 
208 inline double frac(double n) { return n - floor(n); }
UNARY_NUMERIC_FUNCTION(Frac_Function,frac,frac,fract)209 UNARY_NUMERIC_FUNCTION(Frac_Function, frac, frac, fract)
210 
211 inline double sign(double n) { return copysign(double(n!=0),n); }
212 UNARY_NUMERIC_FUNCTION(Sign_Function, sign, sign, sign)
213 
214 UNARY_NUMERIC_FUNCTION(Sin_Function, sin, sin, sin)
215 UNARY_NUMERIC_FUNCTION(Cos_Function, cos, cos, cos)
216 UNARY_NUMERIC_FUNCTION(Tan_Function, tan, tan, tan)
217 UNARY_NUMERIC_FUNCTION(Acos_Function, acos, acos, acos)
218 UNARY_NUMERIC_FUNCTION(Asin_Function, asin, asin, asin)
219 UNARY_NUMERIC_FUNCTION(Atan_Function, atan, atan, atan)
220 
221 UNARY_NUMERIC_FUNCTION(Sinh_Function, sinh, sinh, sinh)
222 UNARY_NUMERIC_FUNCTION(Cosh_Function, cosh, cosh, cosh)
223 UNARY_NUMERIC_FUNCTION(Tanh_Function, tanh, tanh, tanh)
224 UNARY_NUMERIC_FUNCTION(Acosh_Function, acosh, acosh, acosh)
225 UNARY_NUMERIC_FUNCTION(Asinh_Function, asinh, asinh, asinh)
226 UNARY_NUMERIC_FUNCTION(Atanh_Function, atanh, atanh, atanh)
227 
228 struct Phase_Prim : public Unary_Vec2_To_Num_Prim
229 {
namecurv::Phase_Prim230     static const char* name() { return "phase"; }
callcurv::Phase_Prim231     static Value call(Vec2 v, const Context&) { return {atan2(v.y,v.x)}; }
sc_callcurv::Phase_Prim232     static SC_Value sc_call(SC_Frame& fm, SC_Value arg) {
233         auto result = fm.sc_.newvalue(SC_Type::Num());
234         fm.sc_.out() << "  " << result.type << " " << result << " = "
235             << "atan(" << arg << ".y," << arg << ".x);\n";
236         return result;
237     }
238 };
239 using Phase_Function = Unary_Array_Func<Phase_Prim>;
240 
241 struct Max_Prim : public Binary_Num_SCVec_Prim
242 {
namecurv::Max_Prim243     static const char* name() { return "max"; }
zerocurv::Max_Prim244     static Value zero() { return {-INFINITY}; }
callcurv::Max_Prim245     static Value call(double x, double y, const Context&)
246         { return {std::max(x,y)}; }
sc_callcurv::Max_Prim247     static SC_Value sc_call(SC_Frame& fm, SC_Value x, SC_Value y)
248         { return sc_bincall(fm, x.type, "max", x, y); }
249 };
250 using Max_Function = Monoid_Func<Max_Prim>;
251 
252 struct Min_Prim : public Binary_Num_SCVec_Prim
253 {
namecurv::Min_Prim254     static const char* name() { return "min"; }
zerocurv::Min_Prim255     static Value zero() { return {INFINITY}; }
callcurv::Min_Prim256     static Value call(double x, double y, const Context&)
257         { return {std::min(x,y)}; }
sc_callcurv::Min_Prim258     static SC_Value sc_call(SC_Frame& fm, SC_Value x, SC_Value y)
259         { return sc_bincall(fm, x.type, "min", x, y); }
260 };
261 using Min_Function = Monoid_Func<Min_Prim>;
262 
263 using Sum_Function = Monoid_Func<Add_Prim>;
264 
265 using Not_Function = Unary_Array_Func<Not_Prim>;
266 
267 #define BOOL_OP(CppName,Name,Zero,LogOp,BitOp)\
268 struct CppName##_Prim : public Binary_Bool_Prim\
269 {\
270     static const char* name() { return Name; } \
271     static Value zero() { return {Zero}; }\
272     static Value call(bool x, bool y, const Context&) { return {x LogOp y}; }\
273     static SC_Value sc_call(SC_Frame& fm, SC_Value x, SC_Value y)\
274     {\
275         auto result = fm.sc_.newvalue(x.type);\
276         fm.sc_.out() << "  " << x.type << " " << result << " = ";\
277         if (x.type.is_bool())\
278             fm.sc_.out() << x << #LogOp << y << ";\n";\
279         else if (x.type.is_bool_or_vec()) {\
280             /* In GLSL 4.6, I *think* you can use '&' and '|' instead. */ \
281             /* TODO: SubCurv: more efficient and|or in bvec case */ \
282             bool first = true;\
283             fm.sc_.out() << x.type << "(";\
284             for (unsigned i = 0; i < x.type.count(); ++i) {\
285                 if (!first) fm.sc_.out() << ",";\
286                 first = false;\
287                 fm.sc_.out() << x << "[" << i << "]"\
288                     << #LogOp << y << "[" << i << "]";\
289             }\
290             fm.sc_.out() << ")";\
291         }\
292         else\
293             fm.sc_.out() << x << #BitOp << y << ";\n";\
294         fm.sc_.out() << ";\n";\
295         return result;\
296     }\
297 };\
298 using CppName##_Function = Monoid_Func<CppName##_Prim>;\
299 
300 BOOL_OP(And,"and",true,&&,&)
301 BOOL_OP(Or,"or",false,||,|)
302 
303 struct Xor_Prim : public Binary_Bool_Prim
304 {
namecurv::Xor_Prim305     static const char* name() { return "xor"; }
zerocurv::Xor_Prim306     static Value zero() { return {false}; }
callcurv::Xor_Prim307     static Value call(bool x, bool y, const Context&) { return {x != y}; }
sc_callcurv::Xor_Prim308     static SC_Value sc_call(SC_Frame& fm, SC_Value x, SC_Value y)
309     {
310         auto result = fm.sc_.newvalue(x.type);
311         fm.sc_.out() << "  " << result.type << " " << result << " = ";
312         if (x.type.is_bool())
313             fm.sc_.out() << x << "!=" << y;
314         else if (x.type.is_bool_or_vec())
315             fm.sc_.out() << "notEqual(" << x << "," << y << ")";
316         else // bool32 or vector of bool32
317             fm.sc_.out() << x << "^" << y;
318         fm.sc_.out() << ";\n";
319         return result;
320     }
321 };
322 using Xor_Function = Monoid_Func<Xor_Prim>;
323 
324 struct Lshift_Prim : public Shift_Prim
325 {
namecurv::Lshift_Prim326     static const char* name() { return "lshift"; }
callcurv::Lshift_Prim327     static Value call(Shared<const List> a, double b, const Context &cx)
328     {
329         At_Index acx(0, cx);
330         At_Index bcx(1, cx);
331         unsigned n = (unsigned) num_to_int(b, 0, a->size()-1, bcx);
332         Shared<List> result = List::make(a->size());
333         for (unsigned i = 0; i < n; ++i)
334             result->at(i) = {false};
335         for (unsigned i = n; i < a->size(); ++i)
336             result->at(i) = {a->at(i-n).to_bool(acx)};
337         return {result};
338     }
sc_callcurv::Lshift_Prim339     static SC_Value sc_call(SC_Frame& fm, SC_Value x, SC_Value y)
340     {
341         auto result = fm.sc_.newvalue(x.type);
342         fm.sc_.out() << "  " << x.type << " " << result << " = "
343             << x << " << int(" << y << ");\n";
344         return result;
345     }
346 };
347 using Lshift_Function = Binary_Array_Func<Lshift_Prim>;
348 
349 struct Rshift_Prim : public Shift_Prim
350 {
namecurv::Rshift_Prim351     static const char* name() { return "rshift"; }
callcurv::Rshift_Prim352     static Value call(Shared<const List> a, double b, const Context &cx)
353     {
354         At_Index acx(0, cx);
355         At_Index bcx(1, cx);
356         unsigned n = (unsigned) num_to_int(b, 0, a->size()-1, bcx);
357         Shared<List> result = List::make(a->size());
358         for (unsigned i = a->size()-n; i < a->size(); ++i)
359             result->at(i) = {false};
360         for (unsigned i = 0; i < a->size()-n; ++i)
361             result->at(i) = {a->at(i+n).to_bool(acx)};
362         return {result};
363     }
sc_callcurv::Rshift_Prim364     static SC_Value sc_call(SC_Frame& fm, SC_Value x, SC_Value y)
365     {
366         auto result = fm.sc_.newvalue(x.type);
367         fm.sc_.out() << "  " << x.type << " " << result << " = "
368             << x << " >> int(" << y << ");\n";
369         return result;
370     }
371 };
372 using Rshift_Function = Binary_Array_Func<Rshift_Prim>;
373 
374 struct Bool32_Sum_Prim : public Binary_Bool32_Prim
375 {
namecurv::Bool32_Sum_Prim376     static const char* name() { return "bool32_sum"; }
zerocurv::Bool32_Sum_Prim377     static Value zero()
378     {
379         static Value z = {nat_to_bool32(0)};
380         return z;
381     }
callcurv::Bool32_Sum_Prim382     static Value call(unsigned a, unsigned b, const Context&)
383     {
384         return {nat_to_bool32(a + b)};
385     }
sc_callcurv::Bool32_Sum_Prim386     static SC_Value sc_call(SC_Frame& fm, SC_Value x, SC_Value y)
387     {
388         auto result = fm.sc_.newvalue(x.type);
389         fm.sc_.out() << "  " << x.type << " " << result << " = "
390             << x << " + " << y << ";\n";
391         return result;
392     }
393 };
394 using Bool32_Sum_Function = Monoid_Func<Bool32_Sum_Prim>;
395 
396 struct Bool32_Product_Prim : public Binary_Bool32_Prim
397 {
namecurv::Bool32_Product_Prim398     static const char* name() { return "bool32_product"; }
zerocurv::Bool32_Product_Prim399     static Value zero()
400     {
401         static Value z = {nat_to_bool32(1)};
402         return z;
403     }
callcurv::Bool32_Product_Prim404     static Value call(unsigned a, unsigned b, const Context&)
405     {
406         return {nat_to_bool32(a * b)};
407     }
sc_callcurv::Bool32_Product_Prim408     static SC_Value sc_call(SC_Frame& fm, SC_Value x, SC_Value y)
409     {
410         auto result = fm.sc_.newvalue(x.type);
411         fm.sc_.out() << "  " << x.type << " " << result << " = "
412             << x << " * " << y << ";\n";
413         return result;
414     }
415 };
416 using Bool32_Product_Function = Monoid_Func<Bool32_Product_Prim>;
417 
418 struct Bool32_To_Nat_Function : public Function
419 {
420     using Function::Function;
421     struct Prim : public Unary_Bool32_To_Num_Prim
422     {
namecurv::Bool32_To_Nat_Function::Prim423         static const char* name() { return "bool32_to_nat"; }
callcurv::Bool32_To_Nat_Function::Prim424         static Value call(unsigned n, const Context&)
425         {
426             return {double(n)};
427         }
428         // No SubCurv support because a Num (32 bit float)
429         // cannot hold a Nat (32 bit natural).
sc_result_typecurv::Bool32_To_Nat_Function::Prim430         static SC_Type sc_result_type(SC_Type) { return {}; }
sc_callcurv::Bool32_To_Nat_Function::Prim431         static SC_Value sc_call(SC_Frame& fm, SC_Value x)
432         {
433             throw Exception(At_SC_Frame(fm),
434                 "bool32_to_nat is not supported by SubCurv");
435         }
436     };
437     static Unary_Array_Op<Prim> array_op;
callcurv::Bool32_To_Nat_Function438     Value call(Value arg, Fail fl, Frame& fm) const override
439     {
440         return array_op.call(fl, At_Arg(*this, fm), arg);
441     }
442 };
443 
444 struct Nat_To_Bool32_Function : public Function
445 {
446     using Function::Function;
447     struct Prim : public Unary_Num_To_Bool32_Prim
448     {
namecurv::Nat_To_Bool32_Function::Prim449         static const char* name() { return "nat_to_bool32"; }
callcurv::Nat_To_Bool32_Function::Prim450         static Value call(double n, const Context& cx)
451         {
452             return {nat_to_bool32(num_to_nat(n, cx))};
453         }
sc_callcurv::Nat_To_Bool32_Function::Prim454         static SC_Value sc_call(SC_Frame& fm, SC_Value x)
455         {
456             throw Exception(At_SC_Frame(fm),
457                 "nat_to_bool32 can't be called in this context: "
458                 "argument must be a constant");
459         }
460     };
461     static Unary_Array_Op<Prim> array_op;
callcurv::Nat_To_Bool32_Function462     Value call(Value arg, Fail fl, Frame& fm) const override
463     {
464         return array_op.call(fl, At_Arg(*this, fm), arg);
465     }
sc_call_exprcurv::Nat_To_Bool32_Function466     SC_Value sc_call_expr(Operation& argx, Shared<const Phrase> ph, SC_Frame& fm)
467     const override
468     {
469         At_SC_Arg_Expr cx(*this, ph, fm);
470         if (auto k = dynamic_cast<const Constant*>(&argx)) {
471             unsigned n = num_to_nat(k->value_.to_num(cx), cx);
472             auto type = SC_Type::Bool32();
473             auto result = fm.sc_.newvalue(type);
474             fm.sc_.out() << "  " << type << " " << result << " = "
475                 << n << "u;\n";
476             return result;
477         }
478         else {
479             throw Exception(cx, "argument must be a constant");
480         }
481     }
482 };
483 
484 struct Bool32_To_Float_Prim : public Unary_Bool32_To_Num_Prim
485 {
namecurv::Bool32_To_Float_Prim486     static const char* name() { return "bool32_to_float"; }
callcurv::Bool32_To_Float_Prim487     static Value call(unsigned n, const Context&)
488     {
489         return {bitcast_nat_to_float(n)};
490     }
sc_callcurv::Bool32_To_Float_Prim491     static SC_Value sc_call(SC_Frame& fm, SC_Value x)
492     {
493         unsigned count = x.type.is_bool32() ? 1 : x.type.count();
494         auto result = fm.sc_.newvalue(SC_Type::Num(count));
495         fm.sc_.out() << "  " << result.type << " " << result
496             << " = uintBitsToFloat(" << x << ");\n";
497         return result;
498     }
499 };
500 using Bool32_To_Float_Function = Unary_Array_Func<Bool32_To_Float_Prim>;
501 
502 struct Float_To_Bool32_Prim : public Unary_Num_To_Bool32_Prim
503 {
namecurv::Float_To_Bool32_Prim504     static const char* name() { return "bool32_to_float"; }
callcurv::Float_To_Bool32_Prim505     static Value call(double n, const Context&)
506     {
507         return {nat_to_bool32(bitcast_float_to_nat(n))};
508     }
sc_callcurv::Float_To_Bool32_Prim509     static SC_Value sc_call(SC_Frame& fm, SC_Value x)
510     {
511         auto result = fm.sc_.newvalue(SC_Type::Bool32(x.type.count()));
512         fm.sc_.out() << "  " << result.type << " " << result
513             << " = floatBitsToUint(" << x << ");\n";
514         return result;
515     }
516 };
517 using Float_To_Bool32_Function = Unary_Array_Func<Float_To_Bool32_Prim>;
518 
519 Value
select(Value a,Value b,Value c,Fail fl,const Context & cx)520 select(Value a, Value b, Value c, Fail fl, const Context& cx)
521 {
522     if (a.is_bool())
523         return a.to_bool_unsafe() ? b : c;
524     if (auto alist = a.maybe<List>()) {
525         auto blist = b.maybe<Abstract_List>();
526         if (blist) {
527             ASSERT_SIZE(fl,missing,blist,alist->size(),At_Index(1, cx));
528         }
529         auto clist = c.maybe<Abstract_List>();
530         if (clist) {
531             ASSERT_SIZE(fl,missing,clist,alist->size(),At_Index(2, cx));
532         }
533         List_Builder lb;
534         for (unsigned i = 0; i < alist->size(); ++i) {
535             TRY_DEF(v, select(alist->at(i),
536                               blist ? blist->val_at(i) : b,
537                               clist ? clist->val_at(i) : c,
538                               fl, cx));
539             lb.push_back(v);
540         }
541         return lb.get_value();
542     }
543     FAIL(fl, missing, At_Index(0, cx),
544         stringify(a, " is not a Bool or a List"));
545 }
546 
547 // `select[a,b,c]` is a vectorized version of `if` in which the condition is
548 // an array of booleans.
549 // * If `a` is boolean, then the result is `if (a) b else c`.
550 // * If `a` is an array of booleans, then the result is an array with the same
551 //   shape as `a`. The elements of the result are selected from the arrays `b`
552 //   and `c` based on whether the corresponding element of `a` is true or false.
553 //   For example, `select[[false,true], [1,2], [10,20]] == [10,2]`.
554 // * Broadcasting is supported between `a` and `b` and between `a` and `c`,
555 //   so for example, `select[[false,true], 1, 0] == [0,1]`.
556 // * Broadcasting is not supported between `b` and `c`, so for example,
557 //   `select[true, 1, [1,2,3]]` yields `1`, and not `[1,1,1]`.
558 // The SubCurv version of `select` restricts the arguments `b` and `c`
559 // to have the same type.
560 //
561 // `select` has a different name from `if` because it violates some of the
562 // laws of `if`: it always evaluates all 3 arguments, and it can't be involved
563 // in tail recursion optimization.
564 //
565 // Similar to: numpy.where, R `ifelse`
566 struct Select_Function : public Tuple_Function
567 {
Select_Functioncurv::Select_Function568     Select_Function(const char* nm) : Tuple_Function(3,nm) {}
tuple_callcurv::Select_Function569     Value tuple_call(Fail fl, Frame& args) const override
570     {
571         return select(args[0], args[1], args[2], fl, At_Arg(*this, args));
572     }
sc_tuple_callcurv::Select_Function573     SC_Value sc_tuple_call(SC_Frame& fm) const override
574     {
575         auto cond = fm[0];
576         auto consequent = fm[1];
577         auto alternate = fm[2];
578         if (!cond.type.is_bool_or_vec()) {
579             throw Exception(At_SC_Tuple_Arg(0,fm), stringify(
580                 "argument is not bool or bool vector; it has type ",
581                 cond.type));
582         }
583         if (consequent.type != alternate.type) {
584             throw Exception(At_SC_Tuple_Arg(1,fm), stringify(
585                 "2nd and 3rd argument of 'select' have different types: ",
586                 consequent.type, " and ", alternate.type));
587         }
588         SC_Value result;
589         if (cond.type.is_bool()) {
590             result = fm.sc_.newvalue(consequent.type);
591             fm.sc_.out() << "  " << result.type << " " << result << " = ";
592             fm.sc_.out() << cond << "?" << consequent << ":" << alternate;
593         } else {
594             // 'cond' is a boolean vector.
595             if (consequent.type.count() == 1) {
596                 // Consequent & alternate are scalars. Convert them to vectors.
597                 auto T = SC_Type::List(consequent.type, cond.type.count());
598                 sc_try_extend(fm, consequent, T);
599                 sc_try_extend(fm, alternate, T);
600             }
601             else if (!consequent.type.is_vec()) {
602                 throw Exception(At_SC_Tuple_Arg(1,fm), stringify(
603                     "Must be a scalar or vector to match condition argument."
604                     " Instead, type is ", consequent.type));
605             }
606             else if (cond.type.count() != consequent.type.count()) {
607                 throw Exception(At_SC_Tuple_Arg(1,fm), stringify(
608                     "Vector length ",consequent.type.count()," does not match"
609                     " length of condition vector (", cond.type.count(),")"));
610             }
611             result = fm.sc_.newvalue(consequent.type);
612             fm.sc_.out() << "  " << result.type << " " << result << " = ";
613             // In GLSL 4.5, this is `mix(alt,cons,cond)` (all args are vectors).
614             // Right now, we are locked to GLSL 3.3, so we can't use this.
615             // TODO: SubCurv: more efficient `select` for vector case
616             if (result.type.is_num_vec()) {
617                 // This version of 'mix' is linear interpolation: it works by
618                 // multiplication and addition of all 3 arguments. Which is
619                 // different from the boolean vector 'mix' in GLSL 4.5 (which
620                 // produces exact results even when linear interpolation would
621                 // fail due to floating point approximation). But I saw IQ use
622                 // linear interpolation of vectors to implement a 'select' in
623                 // WebGL, so maybe this is efficient code.
624                 fm.sc_.out() << "mix(" << alternate << "," << consequent
625                     << ",vec" << cond.type.count() << "(" << cond << "))";
626             } else {
627                 fm.sc_.out() << result.type << "(";
628                 bool atfirst = true;
629                 for (unsigned i = 0; i < result.type.count(); ++i) {
630                     if (!atfirst) fm.sc_.out() << ",";
631                     atfirst = false;
632                     fm.sc_.out() << cond << "[" << i << "] ? "
633                                 << consequent << "[" << i << "] : "
634                                 << alternate << "[" << i << "]";
635                 }
636                 fm.sc_.out() << ")";
637             }
638         }
639         fm.sc_.out() << ";\n";
640         return result;
641     }
642 };
643 
sc_eval(SC_Frame & fm) const644 SC_Value Equal_Expr::sc_eval(SC_Frame& fm) const
645 {
646     auto a = sc_eval_op(fm, *arg1_);
647     auto b = sc_eval_op(fm, *arg2_);
648     if (a.type != b.type || a.type.plex_array_rank() > 0) {
649         throw Exception(At_SC_Phrase(syntax_, fm),
650             stringify("domain error: ",a.type," == ",b.type));
651     }
652     SC_Value result = fm.sc_.newvalue(SC_Type::Bool());
653     fm.sc_.out() <<"  bool "<<result<<" =("<<a<<" == "<<b<<");\n";
654     return result;
655 }
sc_eval(SC_Frame & fm) const656 SC_Value Not_Equal_Expr::sc_eval(SC_Frame& fm) const
657 {
658     auto a = sc_eval_op(fm, *arg1_);
659     auto b = sc_eval_op(fm, *arg2_);
660     if (a.type != b.type || a.type.plex_array_rank() > 0) {
661         throw Exception(At_SC_Phrase(syntax_, fm),
662             stringify("domain error: ",a.type," != ",b.type));
663     }
664     SC_Value result = fm.sc_.newvalue(SC_Type::Bool());
665     fm.sc_.out() <<"  bool "<<result<<" =("<<a<<" != "<<b<<");\n";
666     return result;
667 }
668 
669 // Generalized dot product that includes vector dot product and matrix product.
670 // Same as Mathematica Dot[A,B]. Like APL A+.×B, Python numpy.dot(A,B)
671 //  dot(a,b) =
672 //    if (count a > 0 && is_list(a[0]))
673 //      [for (row in a) dot(row,b)]  // matrix*...
674 //    else
675 //      sum(a*b)                     // vector*...
676 struct Dot_Function : public Tuple_Function
677 {
Dot_Functioncurv::Dot_Function678     Dot_Function(const char* nm) : Tuple_Function(2,nm) {}
679     Value dot(Value a, Value b, Fail, const At_Arg& cx) const;
tuple_callcurv::Dot_Function680     Value tuple_call(Fail fl, Frame& args) const override
681     {
682         return dot(args[0], args[1], fl, At_Arg(*this, args));
683     }
sc_tuple_callcurv::Dot_Function684     SC_Value sc_tuple_call(SC_Frame& fm) const override
685     {
686         auto a = fm[0];
687         auto b = fm[1];
688         if (a.type.is_num_vec() && a.type == b.type)
689             return sc_bincall(fm, SC_Type::Num(), "dot", a, b);
690         if (a.type.is_num_vec() && b.type.is_mat()
691             && a.type.count() == b.type.count())
692         {
693             return sc_binop(fm, a.type, b, "*", a);
694         }
695         if (a.type.is_mat() && b.type.is_num_vec()
696             && a.type.count() == b.type.count())
697         {
698             return sc_binop(fm, b.type, b, "*", a);
699         }
700         if (a.type.is_mat() && b.type.is_mat()
701             && a.type.count() == b.type.count())
702         {
703             return sc_binop(fm, a.type, b, "*", a);
704         }
705         throw Exception(At_SC_Frame(fm), "dot: invalid arguments");
706     }
707 };
dot(Value a,Value b,Fail fl,const At_Arg & cx) const708 Value Dot_Function::dot(Value a, Value b, Fail fl, const At_Arg& cx) const
709 {
710     auto av = a.maybe<List>();
711     auto bv = b.maybe<List>();
712     if (av && bv) {
713         if (av->size() > 0 && av->at(0).maybe<List>()) {
714             Shared<List> result = List::make(av->size());
715             for (size_t i = 0; i < av->size(); ++i) {
716                 TRY_DEF(v, dot(av->at(i), b, fl, cx));
717                 result->at(i) = v;
718             }
719             return {result};
720         } else {
721             if (av->size() != bv->size())
722                 throw Exception(cx, stringify("list of size ",av->size(),
723                     " can't be multiplied by list of size ",bv->size()));
724             Value result = {0.0};
725             for (size_t i = 0; i < av->size(); ++i) {
726                 TRY_DEF(prod, Multiply_Op::call(fl, cx, av->at(i), bv->at(i)));
727                 TRY_DEF(sum, Add_Op::call(fl, cx, result, prod));
728                 result = sum;
729             }
730             return result;
731         }
732     }
733     // Handle the case where a or b is a reactive list,
734     // and return a reactive result.
735     //   This is copied and modified from Dot_Function::sc_tuple_call.
736     //   The code ought to be identical in both cases.
737     //   The reactive result should contain SubCurv IR code,
738     //   which is simultaneously code that can be evaluated by the interpreter.
739     auto aty = sc_type_of(a);
740     auto bty = sc_type_of(b);
741     SC_Type rty;
742     if (aty.is_num_vec() && aty == bty)
743         rty = SC_Type::Num();
744     else if (aty.is_num_vec() && bty.is_mat() && aty.count() == bty.count())
745         rty = bty;
746     else if (aty.is_mat() && bty.is_num_vec() && aty.count() == bty.count())
747         rty = aty;
748     else if (aty.is_mat() && bty.is_mat() && aty.count() == bty.count())
749         rty = aty;
750     else
751         throw Exception(cx, stringify("dot[",a,",",b,"]: invalid arguments"));
752     Shared<List_Expr> args = List_Expr::make(
753         {to_expr(a, cx.syntax()), to_expr(b, cx.syntax())},
754         share(cx.syntax()));
755     args->init();
756     return {make<Reactive_Expression>(
757         rty,
758         make<Call_Expr>(
759             cx.call_frame_.call_phrase_,
760             make<Constant>(
761                 func_part(cx.call_frame_.call_phrase_),
762                 Value{share(*this)}),
763             args),
764         cx)};
765 }
766 
767 struct Mag_Function : public Tuple_Function
768 {
Mag_Functioncurv::Mag_Function769     Mag_Function(const char* nm) : Tuple_Function(1,nm) {}
tuple_callcurv::Mag_Function770     Value tuple_call(Fail fl, Frame& args) const override
771     {
772         // Use hypot() or BLAS DNRM2 or Eigen stableNorm/blueNorm?
773         // Avoids overflow/underflow due to squaring of large/small values.
774         // Slower.  https://forum.kde.org/viewtopic.php?f=74&t=62402
775 
776         // Fast path: assume we have a list of numbers, compute a result.
777         if (auto list = args[0].maybe<List>()) {
778             double sum = 0.0;
779             for (auto val : *list) {
780                 double x = val.to_num_or_nan();
781                 sum += x * x;
782             }
783             if (sum == sum)
784                 return {sqrt(sum)};
785         }
786 
787         // Slow path, return a reactive value or abort.
788         Shared<Operation> arg_op = nullptr;
789         if (auto rx = args[0].maybe<Reactive_Value>()) {
790             if (rx->sctype_.is_num_vec())
791                 arg_op = rx->expr();
792         } else {
793             TRY_DEF(list, args[0].to<List>(fl, At_Arg(*this, args)));
794             Shared<List_Expr> rlist =
795                 List_Expr::make(list->size(),arg_part(args.call_phrase_));
796             arg_op = rlist;
797             for (unsigned i = 0; i < list->size(); ++i) {
798                 Value val = list->at(i);
799                 if (val.is_num()) {
800                     rlist->at(i) =
801                         make<Constant>(arg_part(args.call_phrase_), val);
802                     continue;
803                 }
804                 auto r = val.maybe<Reactive_Value>();
805                 if (r && r->sctype_.is_num()) {
806                     rlist->at(i) = r->expr();
807                     continue;
808                 }
809                 arg_op = nullptr;
810                 break;
811             }
812             if (arg_op) rlist->init();
813         }
814         if (arg_op) {
815             return {make<Reactive_Expression>(
816                 SC_Type::Num(),
817                 make<Call_Expr>(
818                     args.call_phrase_,
819                     make<Constant>(
820                         func_part(args.call_phrase_),
821                         Value{share(*this)}),
822                     arg_op),
823                 At_Arg(*this, args))};
824         }
825         FAIL(fl, missing, At_Arg(*this, args),
826             stringify(args[0],": domain error"));
827     }
sc_tuple_callcurv::Mag_Function828     SC_Value sc_tuple_call(SC_Frame& fm) const override
829     {
830         auto arg = fm[0];
831         if (!arg.type.is_num_vec())
832             throw Exception(At_SC_Tuple_Arg(0, fm), "mag: argument is not a vector");
833         auto result = fm.sc_.newvalue(SC_Type::Num());
834         fm.sc_.out() << "  float "<<result<<" = length("<<arg<<");\n";
835         return result;
836     }
837 };
838 
839 struct Count_Function : public Tuple_Function
840 {
Count_Functioncurv::Count_Function841     Count_Function(const char* nm) : Tuple_Function(1,nm) {}
tuple_callcurv::Count_Function842     Value tuple_call(Fail fl, Frame& args) const override
843     {
844         if (auto list = args[0].maybe<const Abstract_List>())
845             return {double(list->size())};
846         if (auto re = args[0].maybe<const Reactive_Value>()) {
847             if (re->sctype_.is_list())
848                 return {double(re->sctype_.count())};
849         }
850         FAIL(fl, missing, At_Arg(*this, args), "not a list or string");
851     }
sc_tuple_callcurv::Count_Function852     SC_Value sc_tuple_call(SC_Frame& fm) const override
853     {
854         auto arg = fm[0];
855         if (!arg.type.is_list())
856             throw Exception(At_SC_Tuple_Arg(0, fm), "count: argument is not a list");
857         auto result = fm.sc_.newvalue(SC_Type::Num());
858         fm.sc_.out() << "  float "<<result<<" = "<<arg.type.count()<<";\n";
859         return result;
860     }
861 };
862 struct Fields_Function : public Tuple_Function
863 {
Fields_Functioncurv::Fields_Function864     Fields_Function(const char* nm) : Tuple_Function(1,nm) {}
fieldscurv::Fields_Function865     static Value fields(Value arg, Fail fl, const Context& cx)
866     {
867         if (auto record = arg.maybe<const Record>())
868             return {record->fields()};
869       #if 0
870         else if (auto list = arg.maybe<List>()) {
871             Shared<List> result = List::make(list->size());
872             for (unsigned i = 0; i < list->size(); ++i)
873                 result->at(i) = fields(list->at(i), cx);
874             return {result};
875         }
876       #endif
877         else
878             FAIL(fl, missing, cx, stringify(arg, " is not a record"));
879     }
tuple_callcurv::Fields_Function880     Value tuple_call(Fail fl, Frame& args) const override
881     {
882         return fields(args[0], fl, At_Arg(*this, args));
883     }
884 };
885 
886 // Construct a character from an integer or a string of length 1.
887 // Vectorized.
to_char(Value arg,Fail fl,const Context & cx)888 Value to_char(Value arg, Fail fl, const Context& cx)
889 {
890     if (arg.is_num()) {
891         int code;
892         if (!num_to_int(arg.to_num_unsafe(), code, 1, 127, fl, cx))
893             return missing;
894         return Value{char(code)};
895     }
896     else if (auto list = arg.maybe<List>()) {
897         if (list->empty()) return arg;
898         Shared<String> s = make_uninitialized_string(list->size());
899         for (unsigned i = 0; i < list->size(); ++i) {
900             TRY_DEF(val, to_char(list->at(i), fl, cx));
901             if (val.is_char())
902                 s->at(i) = val.to_char_unsafe();
903             else {
904                 Shared<List> result = List::make(list->size());
905                 for (unsigned j = 0; j < i; ++j)
906                     result->at(j) = Value(s->at(j));
907                 result->at(i) = val;
908                 for (unsigned k = i+1; k < list->size(); ++k)
909                     result->at(i) = to_char(list->at(i), fl, cx);
910                 return {result};
911             }
912         }
913         return {s};
914     }
915     else {
916         FAIL(fl, missing, cx,
917             stringify(arg, " is not an integer, or a list or tree of integers"));
918     }
919 }
920 struct Char_Function : public Function
921 {
922     using Function::Function;
callcurv::Char_Function923     virtual Value call(Value arg, Fail fl, Frame& fm) const override
924     {
925         return to_char(arg, fl, At_Arg(*this, fm));
926     }
927 };
ucode(Value arg,Fail fl,const Context & cx)928 Value ucode(Value arg, Fail fl, const Context& cx)
929 {
930     if (arg.is_char())
931         return {(double)(unsigned)arg.to_char_unsafe()};
932     if (auto str = arg.maybe<const String>()) {
933         List_Builder lb;
934         for (size_t i = 0; i < str->size(); ++i)
935             lb.push_back({(double)(unsigned)str->at(i)});
936         return lb.get_value();
937     }
938     if (auto list = arg.maybe<const List>()) {
939         List_Builder lb;
940         for (Value e : *list) {
941             TRY_DEF(r, ucode(e, fl, cx));
942             lb.push_back(r);
943         }
944         return lb.get_value();
945     }
946     FAIL(fl, missing, cx,
947         stringify(arg, " is not a character or list of characters"));
948 }
949 struct Ucode_Function : public Function
950 {
951     using Function::Function;
callcurv::Ucode_Function952     virtual Value call(Value arg, Fail fl, Frame& fm) const override
953     {
954         return ucode(arg, fl, At_Arg(*this, fm));
955     }
956 };
957 struct Symbol_Function : public Function
958 {
959     using Function::Function;
callcurv::Symbol_Function960     virtual Value call(Value arg, Fail fl, Frame& fm) const override
961     {
962         At_Arg cx(*this, fm);
963         TRY_DEF(string, value_to_string(arg, fl, cx));
964         for (auto c : *string) {
965             if (c <= ' ' || c >= '~') {
966                 FAIL(fl,missing,cx, stringify(
967                     "string ",arg," contains ",illegal_character_message(c)));
968             }
969         }
970         auto symbol = make_symbol(string->data(), string->size());
971         return symbol.to_value();
972     }
973 };
974 struct String_Function : public Function
975 {
976     using Function::Function;
callcurv::String_Function977     virtual Value call(Value arg, Fail fl, Frame& fm) const override
978     {
979         String_Builder sb;
980         arg.print_string(sb);
981         return sb.get_value();
982     }
983 };
984 struct Repr_Function : public Tuple_Function
985 {
Repr_Functioncurv::Repr_Function986     Repr_Function(const char* nm) : Tuple_Function(1,nm) {}
tuple_callcurv::Repr_Function987     Value tuple_call(Fail, Frame& args) const override
988     {
989         String_Builder sb;
990         sb << args[0];
991         return sb.get_value();
992     }
993 };
994 
995 struct Match_Function : public Function
996 {
997     using Function::Function;
callcurv::Match_Function998     virtual Value call(Value arg, Fail fl, Frame& fm) const override
999     {
1000         At_Arg ctx0(*this, fm);
1001         TRY_DEF(list, arg.to<List>(fl, ctx0));
1002         std::vector<Shared<const Function>> cases;
1003         for (size_t i = 0; i < list->size(); ++i) {
1004             TRY_DEF(fn, value_to_function(list->at(i), fl, At_Index(i,ctx0)));
1005             cases.push_back(fn);
1006         }
1007         auto mf = make<Piecewise_Function>(cases);
1008         mf->name_ = name_;
1009         mf->argpos_ = 1;
1010         return {mf};
1011     }
1012 };
1013 
1014 struct Compose_Function : public Function
1015 {
1016     using Function::Function;
callcurv::Compose_Function1017     virtual Value call(Value arg, Fail fl, Frame& fm) const override
1018     {
1019         At_Arg ctx0(*this, fm);
1020         TRY_DEF(list, arg.to<List>(fl, ctx0));
1021         std::vector<Shared<const Function>> cases;
1022         for (size_t i = 0; i < list->size(); ++i) {
1023             TRY_DEF(fn, value_to_function(list->at(i), fl, At_Index(i,ctx0)));
1024             cases.push_back(fn);
1025         }
1026         auto mf = make<Composite_Function>(cases);
1027         mf->name_ = name_;
1028         mf->argpos_ = 1;
1029         return {mf};
1030     }
1031 };
1032 
1033 struct TSlice_Function : public Function
1034 {
1035     using Function::Function;
callcurv::TSlice_Function1036     virtual Value call(Value arg, Fail fl, Frame& fm) const override
1037     {
1038         At_Arg cx(*this, fm);
1039         TRY_DEF(list, arg.to<List>(fl, cx));
1040         return make_tslice(list->begin(), list->end());
1041     }
1042 };
1043 struct TPath_Function : public Function
1044 {
1045     using Function::Function;
callcurv::TPath_Function1046     virtual Value call(Value arg, Fail fl, Frame& fm) const override
1047     {
1048         At_Arg cx(*this, fm);
1049         TRY_DEF(list, arg.to<List>(fl, cx));
1050         return make_tpath(list->begin(), list->end());
1051     }
1052 };
1053 struct Amend_Function : public Curried_Function
1054 {
callcurv::Amend_Function1055     static Value call(const Function& fn, Fail, Frame& args)
1056     {
1057         return tree_amend(args[2], args[0], args[1], At_Arg(fn, args));
1058     }
Amend_Functioncurv::Amend_Function1059     Amend_Function(const char* nm) : Curried_Function(3,nm,call) {}
1060 };
1061 
1062 // The filename argument to "file", if it is a relative filename,
1063 // is interpreted relative to the parent directory of the source file from
1064 // which "file" is called.
1065 //
1066 // Because "file" has this hidden parameter (the name of the source file from
1067 // which it is called), it is not a pure function. For this reason, it isn't
1068 // a function value at all, it's a metafunction.
1069 struct File_Expr : public Just_Expression
1070 {
1071     Shared<Operation> arg_;
File_Exprcurv::File_Expr1072     File_Expr(Shared<const Call_Phrase> src, Shared<Operation> arg)
1073     :
1074         Just_Expression(move(src)),
1075         arg_(move(arg))
1076     {}
evalcurv::File_Expr1077     virtual Value eval(Frame& fm) const override
1078     {
1079         // Each call to `file pathname` has its own stack frame,
1080         // which permits calls to `file pathname` to appear in stack traces.
1081         auto& callphrase = dynamic_cast<const Call_Phrase&>(*syntax_);
1082         std::unique_ptr<Frame> f2 =
1083             Frame::make(0, fm.sstate_, &fm, fm.func_, &callphrase);
1084         At_Metacall_With_Call_Frame cx("file", 0, *f2);
1085 
1086         // construct file pathname from argument
1087         Value arg = arg_->eval(fm);
1088         auto argstr = value_to_string(arg, Fail::hard, cx);
1089         namespace fs = boost::filesystem;
1090         fs::path filepath;
1091         auto caller_filename = syntax_->location().source().name_;
1092         if (caller_filename->empty()) {
1093             filepath = fs::path(argstr->c_str());
1094         } else {
1095             filepath = fs::path(caller_filename->c_str()).parent_path()
1096                 / fs::path(argstr->c_str());
1097         }
1098 
1099         return import_value(import, filepath, cx);
1100     }
1101 };
1102 struct File_Metafunction : public Metafunction
1103 {
1104     using Metafunction::Metafunction;
callcurv::File_Metafunction1105     virtual Shared<Meaning> call(const Call_Phrase& ph, Environ& env) override
1106     {
1107         return make<File_Expr>(share(ph), analyse_op(*ph.arg_, env));
1108     }
print_helpcurv::File_Metafunction1109     virtual void print_help(std::ostream& out) const override
1110     {
1111         out <<
1112             "file <filename>\n"
1113             "  Evaluate the program stored in the file named <filename>, and return the resulting value.\n"
1114             "  <filename> is a string.\n";
1115     }
1116 };
1117 
1118 /// The meaning of a call to `print`, such as `print "foo"`.
1119 struct Print_Action : public Operation
1120 {
1121     Shared<Operation> arg_;
Print_Actioncurv::Print_Action1122     Print_Action(
1123         Shared<const Phrase> syntax,
1124         Shared<Operation> arg)
1125     :
1126         Operation(move(syntax)),
1127         arg_(move(arg))
1128     {}
execcurv::Print_Action1129     virtual void exec(Frame& fm, Executor&) const override
1130     {
1131         Value arg = arg_->eval(fm);
1132         auto str = to_print_string(arg);
1133         fm.sstate_.system_.print(str->c_str());
1134     }
1135 };
1136 /// The meaning of the phrase `print` in isolation.
1137 struct Print_Metafunction : public Metafunction
1138 {
1139     using Metafunction::Metafunction;
callcurv::Print_Metafunction1140     virtual Shared<Meaning> call(const Call_Phrase& ph, Environ& env) override
1141     {
1142         return make<Print_Action>(share(ph), analyse_op(*ph.arg_, env));
1143     }
print_helpcurv::Print_Metafunction1144     virtual void print_help(std::ostream& out) const override
1145     {
1146         out <<
1147             "print <message>\n"
1148             "  Print a message string on the debug console, followed by newline. If <message> is not a string,\n"
1149             "  it is converted to a string using the 'string' function.\n";
1150     }
1151 };
1152 
1153 struct Warning_Action : public Operation
1154 {
1155     Shared<Operation> arg_;
Warning_Actioncurv::Warning_Action1156     Warning_Action(
1157         Shared<const Phrase> syntax,
1158         Shared<Operation> arg)
1159     :
1160         Operation(move(syntax)),
1161         arg_(move(arg))
1162     {}
execcurv::Warning_Action1163     virtual void exec(Frame& fm, Executor&) const override
1164     {
1165         Value arg = arg_->eval(fm);
1166         auto msg = to_print_string(arg);
1167         Exception exc{At_Phrase(*syntax_, fm), msg};
1168         fm.sstate_.system_.warning(exc);
1169     }
1170 };
1171 /// The meaning of the phrase `warning` in isolation.
1172 struct Warning_Metafunction : public Metafunction
1173 {
1174     using Metafunction::Metafunction;
callcurv::Warning_Metafunction1175     virtual Shared<Meaning> call(const Call_Phrase& ph, Environ& env) override
1176     {
1177         return make<Warning_Action>(share(ph), analyse_op(*ph.arg_, env));
1178     }
print_helpcurv::Warning_Metafunction1179     virtual void print_help(std::ostream& out) const override
1180     {
1181         out <<
1182             "warning <message>\n"
1183             "  Print a message string on the debug console, preceded by 'WARNING: ',\n"
1184             "  followed by newline and then a stack trace. If <message> is not a string,\n"
1185             "  it is converted to a string using the 'string' function.\n";
1186     }
1187 };
1188 
1189 struct Error_Function : public Function
1190 {
1191     using Function::Function;
callcurv::Error_Function1192     virtual Value call(Value arg, Fail fl, Frame& fm) const override
1193     {
1194         FAIL(fl, missing, At_Frame(fm), to_print_string(arg));
1195     }
1196 };
1197 /// The meaning of a call to `error`, such as `error("foo")`.
1198 struct Error_Operation : public Operation
1199 {
1200     Shared<Operation> arg_;
Error_Operationcurv::Error_Operation1201     Error_Operation(
1202         Shared<const Phrase> syntax,
1203         Shared<Operation> arg)
1204     :
1205         Operation(move(syntax)),
1206         arg_(move(arg))
1207     {}
runcurv::Error_Operation1208     [[noreturn]] void run(Frame& fm) const
1209     {
1210         Value val = arg_->eval(fm);
1211         auto msg = to_print_string(val);
1212         throw Exception{At_Phrase(*syntax_, fm), msg};
1213     }
execcurv::Error_Operation1214     virtual void exec(Frame& fm, Executor&) const override
1215     {
1216         run(fm);
1217     }
evalcurv::Error_Operation1218     virtual Value eval(Frame& fm) const override
1219     {
1220         run(fm);
1221     }
1222 };
1223 /// The meaning of the phrase `error` in isolation.
1224 struct Error_Metafunction : public Metafunction
1225 {
1226     using Metafunction::Metafunction;
callcurv::Error_Metafunction1227     virtual Shared<Meaning> call(const Call_Phrase& ph, Environ& env) override
1228     {
1229         return make<Error_Operation>(share(ph), analyse_op(*ph.arg_, env));
1230     }
to_operationcurv::Error_Metafunction1231     virtual Shared<Operation> to_operation(Source_State&) override
1232     {
1233         return make<Constant>(syntax_, Value{make<Error_Function>("error")});
1234     }
print_helpcurv::Error_Metafunction1235     virtual void print_help(std::ostream& out) const override
1236     {
1237         out <<
1238             "error <message>\n"
1239             "  On the debug console, print 'ERROR: ', then the message string, then newline and a stack trace.\n"
1240             "  Then terminate the program. If <message> is not a string,\n"
1241             "  convert it to a string using the 'string' function.\n";
1242     }
1243 };
1244 
1245 // exec(expr) -- a debug action that evaluates expr, then discards the result.
1246 // It is used to call functions or source files for their side effects.
1247 struct Exec_Action : public Operation
1248 {
1249     Shared<Operation> arg_;
Exec_Actioncurv::Exec_Action1250     Exec_Action(
1251         Shared<const Phrase> syntax,
1252         Shared<Operation> arg)
1253     :
1254         Operation(move(syntax)),
1255         arg_(move(arg))
1256     {}
execcurv::Exec_Action1257     virtual void exec(Frame& fm, Executor&) const override
1258     {
1259         arg_->eval(fm);
1260     }
1261 };
1262 struct Exec_Metafunction : public Metafunction
1263 {
1264     using Metafunction::Metafunction;
callcurv::Exec_Metafunction1265     virtual Shared<Meaning> call(const Call_Phrase& ph, Environ& env) override
1266     {
1267         return make<Exec_Action>(share(ph), analyse_op(*ph.arg_, env));
1268     }
print_helpcurv::Exec_Metafunction1269     virtual void print_help(std::ostream& out) const override
1270     {
1271         out <<
1272             "exec <expression>\n"
1273             "  Evaluate the expression and then ignore the result. This is used for calling a function whose only\n"
1274             "  purpose is to have a side effect (by executing debug statements) and you don't care about the result.\n";
1275     }
1276 };
1277 
1278 struct Assert_Action : public Operation
1279 {
1280     Shared<Operation> arg_;
Assert_Actioncurv::Assert_Action1281     Assert_Action(
1282         Shared<const Phrase> syntax,
1283         Shared<Operation> arg)
1284     :
1285         Operation(move(syntax)),
1286         arg_(move(arg))
1287     {}
execcurv::Assert_Action1288     virtual void exec(Frame& fm, Executor&) const override
1289     {
1290         At_Metacall cx{"assert", 0, *arg_->syntax_, fm};
1291         bool b = arg_->eval(fm).to_bool(cx);
1292         if (!b)
1293             throw Exception(At_Phrase(*syntax_, fm), "assertion failed");
1294     }
1295 };
1296 struct Assert_Metafunction : public Metafunction
1297 {
1298     using Metafunction::Metafunction;
callcurv::Assert_Metafunction1299     virtual Shared<Meaning> call(const Call_Phrase& ph, Environ& env) override
1300     {
1301         auto arg = analyse_op(*ph.arg_, env);
1302         return make<Assert_Action>(share(ph), arg);
1303     }
print_helpcurv::Assert_Metafunction1304     virtual void print_help(std::ostream& out) const override
1305     {
1306         out <<
1307             "assert <condition>\n"
1308             "  Evaluate the condition, which must be true or false. If it is true, then nothing happens.\n"
1309             "  If it is false, then an assertion failure error message is produced, followed by a stack trace,\n"
1310             "  and the program is terminated.\n";
1311     }
1312 };
1313 
1314 struct Assert_Error_Action : public Operation
1315 {
1316     Shared<Operation> expected_message_;
1317     Shared<const String> actual_message_;
1318     Shared<Operation> expr_;
1319 
Assert_Error_Actioncurv::Assert_Error_Action1320     Assert_Error_Action(
1321         Shared<const Phrase> syntax,
1322         Shared<Operation> expected_message,
1323         Shared<const String> actual_message,
1324         Shared<Operation> expr)
1325     :
1326         Operation(move(syntax)),
1327         expected_message_(move(expected_message)),
1328         actual_message_(move(actual_message)),
1329         expr_(move(expr))
1330     {}
1331 
execcurv::Assert_Error_Action1332     virtual void exec(Frame& fm, Executor&) const override
1333     {
1334         Value expected_msg_val = expected_message_->eval(fm);
1335         auto expected_msg_str =
1336             value_to_string(expected_msg_val, Fail::hard,
1337                 At_Phrase(*expected_message_->syntax_, fm));
1338 
1339         if (actual_message_ != nullptr) {
1340             if (*actual_message_ != *expected_msg_str)
1341                 throw Exception(At_Phrase(*syntax_, fm),
1342                     stringify("assertion failed: expected error \"",
1343                         expected_msg_str,
1344                         "\", actual error \"",
1345                         actual_message_,
1346                         "\""));
1347             return;
1348         }
1349 
1350         Value result;
1351         try {
1352             result = expr_->eval(fm);
1353         } catch (Exception& e) {
1354             if (*e.shared_what() != *expected_msg_str) {
1355                 throw Exception(At_Phrase(*syntax_, fm),
1356                     stringify("assertion failed: expected error \"",
1357                         expected_msg_str,
1358                         "\", actual error \"",
1359                         e.shared_what(),
1360                         "\""));
1361             }
1362             return;
1363         }
1364         throw Exception(At_Phrase(*syntax_, fm),
1365             stringify("assertion failed: expected error \"",
1366                 expected_msg_str,
1367                 "\", got value ", result));
1368     }
1369 };
1370 struct Assert_Error_Metafunction : public Metafunction
1371 {
1372     using Metafunction::Metafunction;
callcurv::Assert_Error_Metafunction1373     virtual Shared<Meaning> call(const Call_Phrase& ph, Environ& env) override
1374     {
1375         Shared<const Comma_Phrase> commas = nullptr;
1376         if (auto parens = cast<Paren_Phrase>(ph.arg_)) {
1377             commas = cast<Comma_Phrase>(parens->body_);
1378         }
1379         else if (auto brackets = cast<Bracket_Phrase>(ph.arg_)) {
1380             commas = cast<Comma_Phrase>(brackets->body_);
1381         }
1382         if (commas && commas->args_.size() == 2) {
1383             auto msg = analyse_op(*commas->args_[0].expr_, env);
1384             Shared<Operation> expr = nullptr;
1385             Shared<const String> actual_msg = nullptr;
1386             try {
1387                 expr = analyse_op(*commas->args_[1].expr_, env);
1388             } catch (Exception& e) {
1389                 actual_msg = e.shared_what();
1390             }
1391             return make<Assert_Error_Action>(share(ph), msg, actual_msg, expr);
1392         } else {
1393             throw Exception(At_Phrase(ph, env),
1394                 "assert_error: expecting 2 arguments");
1395         }
1396     }
print_helpcurv::Assert_Error_Metafunction1397     virtual void print_help(std::ostream& out) const override
1398     {
1399         out <<
1400             "assert_error [error_message_string, expression]\n"
1401             "  Evaluate the expression argument. Assert that the expression evaluation terminates with an error,\n"
1402             "  and that the resulting error message is equal to error_message_string. Used for unit testing.\n";
1403     }
1404 };
1405 
1406 struct Defined_Expression : public Just_Expression
1407 {
1408     Shared<const Operation> expr_;
1409     Symbol_Expr selector_;
1410 
Defined_Expressioncurv::Defined_Expression1411     Defined_Expression(
1412         Shared<const Phrase> syntax,
1413         Shared<const Operation> expr,
1414         Symbol_Expr selector)
1415     :
1416         Just_Expression(move(syntax)),
1417         expr_(move(expr)),
1418         selector_(move(selector))
1419     {
1420     }
1421 
defined_atcurv::Defined_Expression1422     static Value defined_at(Value val, Symbol_Ref id)
1423     {
1424         if (auto rec = val.maybe<Record>())
1425             return {rec->hasfield(id)};
1426       #if 0
1427         else if (auto list = val.maybe<List>()) {
1428             Shared<List> result = List::make(list->size());
1429             for (unsigned i = 0; i < list->size(); ++i)
1430                 result->at(i) = defined_at(list->at(i), id);
1431             return {result};
1432         }
1433       #endif
1434         else
1435             return {false};
1436     }
evalcurv::Defined_Expression1437     virtual Value eval(Frame& fm) const override
1438     {
1439         auto val = expr_->eval(fm);
1440         auto id = selector_.eval(fm);
1441         return defined_at(val, id);
1442     }
1443 };
1444 struct Defined_Metafunction : public Metafunction
1445 {
1446     using Metafunction::Metafunction;
callcurv::Defined_Metafunction1447     virtual Shared<Meaning> call(const Call_Phrase& ph, Environ& env) override
1448     {
1449         auto argph = strip_parens(ph.arg_);
1450         if (auto dot = cast<const Dot_Phrase>(argph)) {
1451             if (auto brackets = cast<const Bracket_Phrase>(dot->right_)) {
1452                 return make<Defined_Expression>(
1453                     share(ph),
1454                     analyse_op(*dot->left_, env),
1455                     Symbol_Expr(analyse_op(*brackets->body_, env)));
1456             }
1457             if (auto string = cast<const String_Phrase>(dot->right_)) {
1458                 env.sstate_.deprecate(
1459                     &Source_State::dot_string_deprecated_, 1,
1460                     At_Phrase(*argph, env),
1461                     Source_State::dot_string_deprecated_msg);
1462             }
1463         }
1464         auto arg = analyse_op(*argph, env);
1465         if (auto dot = cast<Dot_Expr>(arg)) {
1466             return make<Defined_Expression>(
1467                 share(ph), dot->base_, dot->selector_);
1468         }
1469         if (auto slice = cast<Index_Expr>(arg)) {
1470             return make<Defined_Expression>(
1471                 share(ph), slice->arg1_, Symbol_Expr(slice->arg2_));
1472         }
1473         throw Exception(At_Phrase(*argph, env),
1474             "defined: argument must be `expression.identifier`"
1475                 " or `expression.[expression]`"
1476                 " or `expression@expression`");
1477     }
print_helpcurv::Defined_Metafunction1478     virtual void print_help(std::ostream& out) const override
1479     {
1480         out <<
1481             "defined (record . identifier)\n"
1482             "  True if a field named identifier is defined by record, otherwise false.\n"
1483             "  For example, given 'R={a:1}', then 'defined(R.a)' is true and 'defined(R.foo)' is false.\n"
1484             "\n"
1485             "defined (record .[ symbolExpr ])\n"
1486             "  Test the field named by the symbol after evaluating symbolExpr. If the field exists,\n"
1487             "  return true, otherwise false. This allows the field name to be computed at run time.\n"
1488             "  For example, 'defined(R.[#a])' is true and 'defined(R.[#foo])' is false.\n";
1489     }
1490 };
1491 
1492 struct Builtin_Time : public Builtin
1493 {
to_meaningcurv::Builtin_Time1494     virtual Shared<Meaning> to_meaning(const Identifier& id) const
1495     {
1496         return make<Constant>(share(id), Value{make<Uniform_Variable>(
1497             make_symbol("time"),
1498             std::string("u_time"),
1499             SC_Type::Num(),
1500             share(id))});
1501     }
1502 };
1503 
1504 const Namespace&
builtin_namespace()1505 builtin_namespace()
1506 {
1507     #define FUNCTION(nm,f) \
1508         {make_symbol(nm), make<Builtin_Value>(Value{make<f>(nm)})}
1509 
1510     static const Namespace names = {
1511     {make_symbol("pi"), make<Builtin_Value>(pi)},
1512     {make_symbol("tau"), make<Builtin_Value>(two_pi)},
1513     {make_symbol("inf"), make<Builtin_Value>(INFINITY)},
1514     {make_symbol("false"), make<Builtin_Value>(Value(false))},
1515     {make_symbol("true"), make<Builtin_Value>(Value(true))},
1516     {make_symbol("time"), make<Builtin_Time>()},
1517 
1518     FUNCTION("is_bool", Is_Bool_Function),
1519     FUNCTION("is_char", Is_Char_Function),
1520     FUNCTION("is_symbol", Is_Symbol_Function),
1521     FUNCTION("is_num", Is_Num_Function),
1522     FUNCTION("is_string", Is_String_Function),
1523     FUNCTION("is_list", Is_List_Function),
1524     FUNCTION("is_record", Is_Record_Function),
1525     FUNCTION("is_primitive_func", Is_Primitive_Func_Function),
1526     FUNCTION("is_func", Is_Func_Function),
1527     FUNCTION("bit", Bit_Function),
1528     FUNCTION("sqrt", Sqrt_Function),
1529     FUNCTION("log", Log_Function),
1530     FUNCTION("abs", Abs_Function),
1531     FUNCTION("floor", Floor_Function),
1532     FUNCTION("ceil", Ceil_Function),
1533     FUNCTION("trunc", Trunc_Function),
1534     FUNCTION("round", Round_Function),
1535     FUNCTION("frac", Frac_Function),
1536     FUNCTION("sign", Sign_Function),
1537     FUNCTION("sin", Sin_Function),
1538     FUNCTION("cos", Cos_Function),
1539     FUNCTION("tan", Tan_Function),
1540     FUNCTION("asin", Asin_Function),
1541     FUNCTION("acos", Acos_Function),
1542     FUNCTION("atan", Atan_Function),
1543     FUNCTION("phase", Phase_Function),
1544     FUNCTION("sinh", Sinh_Function),
1545     FUNCTION("cosh", Cosh_Function),
1546     FUNCTION("tanh", Tanh_Function),
1547     FUNCTION("asinh", Asinh_Function),
1548     FUNCTION("acosh", Acosh_Function),
1549     FUNCTION("atanh", Atanh_Function),
1550     FUNCTION("max", Max_Function),
1551     FUNCTION("min", Min_Function),
1552     FUNCTION("sum", Sum_Function),
1553     FUNCTION("not", Not_Function),
1554     FUNCTION("and", And_Function),
1555     FUNCTION("or", Or_Function),
1556     FUNCTION("xor", Xor_Function),
1557     FUNCTION("lshift", Lshift_Function),
1558     FUNCTION("rshift", Rshift_Function),
1559     FUNCTION("bool32_sum", Bool32_Sum_Function),
1560     FUNCTION("bool32_product", Bool32_Product_Function),
1561     FUNCTION("bool32_to_nat", Bool32_To_Nat_Function),
1562     FUNCTION("nat_to_bool32", Nat_To_Bool32_Function),
1563     FUNCTION("bool32_to_float", Bool32_To_Float_Function),
1564     FUNCTION("float_to_bool32", Float_To_Bool32_Function),
1565     FUNCTION("select", Select_Function),
1566     FUNCTION("dot", Dot_Function),
1567     FUNCTION("mag", Mag_Function),
1568     FUNCTION("count", Count_Function),
1569     FUNCTION("fields", Fields_Function),
1570     FUNCTION("char", Char_Function),
1571     FUNCTION("ucode", Ucode_Function),
1572     FUNCTION("symbol", Symbol_Function),
1573     FUNCTION("string", String_Function),
1574     FUNCTION("repr", Repr_Function),
1575     FUNCTION("match", Match_Function),
1576     FUNCTION("compose", Compose_Function),
1577 
1578     // top secret index API (aka lenses)
1579     {make_symbol("this"), make<Builtin_Value>(Value{make<This>()})},
1580     FUNCTION("tslice", TSlice_Function),
1581     FUNCTION("tpath", TPath_Function),
1582     FUNCTION("amend", Amend_Function),
1583 
1584     {make_symbol("file"), make<Builtin_Meaning<File_Metafunction>>()},
1585     {make_symbol("print"), make<Builtin_Meaning<Print_Metafunction>>()},
1586     {make_symbol("warning"), make<Builtin_Meaning<Warning_Metafunction>>()},
1587     {make_symbol("error"), make<Builtin_Meaning<Error_Metafunction>>()},
1588     {make_symbol("assert"), make<Builtin_Meaning<Assert_Metafunction>>()},
1589     {make_symbol("assert_error"), make<Builtin_Meaning<Assert_Error_Metafunction>>()},
1590     {make_symbol("exec"), make<Builtin_Meaning<Exec_Metafunction>>()},
1591     {make_symbol("defined"), make<Builtin_Meaning<Defined_Metafunction>>()},
1592     };
1593     return names;
1594 }
1595 
1596 } // namespace curv
1597