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 #ifndef LIBCURV_FUNCTION_H
6 #define LIBCURV_FUNCTION_H
7 
8 #include <libcurv/fail.h>
9 #include <libcurv/list.h>
10 #include <libcurv/meaning.h>
11 #include <libcurv/sc_frame.h>
12 #include <libcurv/value.h>
13 
14 namespace curv {
15 
16 struct Context;
17 
18 /// A function value.
19 struct Function : public Ref_Value
20 {
21     // size of call frame
22     slot_t nslots_;
23 
24     // Suppose this function is the result of partial application of a named
25     // function. Then this is the # of arguments that were applied to get here,
26     // and `name_` is the name of the base function.
27     unsigned argpos_ = 0;
28 
29     // optional name of function
30     Symbol_Ref name_{};
31 
FunctionFunction32     Function(slot_t nslots, Symbol_Ref name)
33     :
34         Ref_Value(ty_function),
35         nslots_(nslots),
36         name_(name)
37     {
38     }
39 
FunctionFunction40     Function(slot_t nslots, const char* name)
41     :
42         Function(nslots, make_symbol(name))
43     {
44     }
45 
FunctionFunction46     Function(slot_t nslots)
47     :
48         Ref_Value(ty_function),
49         nslots_(nslots)
50     {}
51 
FunctionFunction52     Function(const char* name)
53     :
54         Function(0, name)
55     {
56     }
57 
58     // call the function during evaluation
59     virtual Value call(Value, Fail, Frame&) const = 0;
60     virtual void tail_call(Value, std::unique_ptr<Frame>&) const;
61 
62     // Attempt a tail call: return false if the call fails (parameter pattern
63     // doesn't match the value); otherwise call the function and return true.
64     virtual bool try_tail_call(Value, std::unique_ptr<Frame>&) const;
65 
66     // Generate a call to the function during SubCurv compilation.
67     // The argument is represented as an expression.
68     virtual SC_Value sc_call_expr(Operation&, Shared<const Phrase>, SC_Frame&) const;
69 
70     /// Print a value like a Curv expression.
71     virtual void print_repr(std::ostream&) const;
72 
73     static const char name[];
74 };
75 
76 // Returns nullptr if argument is not a function.
77 // If the Value is a record with a `call` field, then we convert the value
78 // of the `call` field to a function by recursively calling `maybe_function`.
79 // May throw an exception if fetching the `call` field fails (currently
80 // only happens for directory records).
81 Shared<const Function> maybe_function(Value, const Context&);
82 
83 // If Value is not a Function, fail.
84 Shared<const Function> value_to_function(Value, Fail, const Context&);
85 
value_to_function(Value val,const Context & cx)86 inline Shared<const Function> value_to_function(Value val, const Context& cx)
87 {
88     return value_to_function(val, Fail::hard, cx);
89 }
90 
91 // Call a function or index into a list or string.
92 // Implements the Curv juxtaposition operator: `func arg`.
93 Value call_func(
94     Value func, Value arg, Shared<const Phrase> call_phrase, Frame&);
95 
96 // A Tuple_Function has a single argument, which is a tuple when nargs!=1.
97 // Tuple functions with a nargs of 0, 1 or 2 are called like this:
98 //   f[], f(x), f[x,y]
99 //
100 // This class is a convenience for defining builtin functions.
101 // The tuple is unpacked into individual values, stored as frame slots,
102 // and an error is thrown if the tuple contains the wrong number of values.
103 // Within `tuple_call(Frame& args)`, use `args[i]` to fetch the i'th argument.
104 // Likewise, in the SC compiler, the Operation argument of sc_call_expr()
105 // is processed into a sequence of SC_Values, stored in the SC_Frame that is
106 // passed to sc_tuple_call().
107 struct Tuple_Function : public Function
108 {
109     unsigned nargs_;
110 
Tuple_FunctionTuple_Function111     Tuple_Function(unsigned nargs, const char* name)
112     :
113         Function(nargs, name),
114         nargs_(nargs)
115     {}
116 
117     // call the function during evaluation, with specified argument value.
118     virtual Value call(Value, Fail, Frame&) const override;
119 
120     // call the function during evaluation, with arguments stored in the frame.
121     virtual Value tuple_call(Fail, Frame& args) const = 0;
122 
123     // Generate a call to the function during SubCurv compilation.
124     // The argument is represented as an expression.
125     virtual SC_Value sc_call_expr(Operation&, Shared<const Phrase>, SC_Frame&)
126         const override;
127 
128     // generate a call to the function during SubCurv compilation
129     virtual SC_Value sc_tuple_call(SC_Frame&) const;
130 };
131 
132 // A Partial_Application is the result of calling a Curried_Function.
133 struct Partial_Application_Base : public Function
134 {
nargsPartial_Application_Base135     unsigned nargs() const { return nslots_; }
136 
Partial_Application_BasePartial_Application_Base137     Partial_Application_Base(unsigned nargs, Symbol_Ref name,
138         Value (*func)(const Function&, Fail, Frame&))
139     :
140         Function(nargs, name),
141         callfunc_(func)
142     {}
143 
144     virtual Value call(Value, Fail, Frame&) const override;
145 
146     Value (*callfunc_)(const Function&, Fail, Frame&);
147     TAIL_ARRAY_MEMBERS(Value)
148 };
149 using Partial_Application = Tail_Array<Partial_Application_Base>;
150 
151 // This is a convenience for defining builtin curried functions.
152 struct Curried_Function : public Function
153 {
nargsCurried_Function154     unsigned nargs() const { return nslots_; }
155     Value (*callfunc_)(const Function&, Fail, Frame&);
156 
Curried_FunctionCurried_Function157     Curried_Function(unsigned nargs, const char* name,
158         Value (*func)(const Function&, Fail, Frame&))
159     :
160         Function(nargs, name),
161         callfunc_(func)
162     {
163         assert(nargs >= 2);
164     }
165 
166     virtual Value call(Value, Fail, Frame&) const override;
167 };
168 
169 /// The run-time representation of a compiled lambda expression.
170 ///
171 /// This is the compile-time component of a function value, minus the
172 /// values of non-local variables, which are captured at run time in a Closure.
173 /// It's not a proper value, but can be stored in a Value slot.
174 struct Lambda : public Ref_Value
175 {
176     Shared<const Pattern> pattern_;
177     Shared<Operation> expr_;
178     slot_t nslots_; // size of call frame
179 
180     // optional name of function
181     Symbol_Ref name_{};
182 
183     // Suppose this function is the result of partial application of a named
184     // function. Then this is the # of arguments that were applied to get here,
185     // and `name_` is the name of the base function.
186     unsigned argpos_ = 0;
187 
LambdaLambda188     Lambda(
189         Shared<const Pattern> pattern,
190         Shared<Operation> expr,
191         slot_t nslots)
192     :
193         Ref_Value(ty_lambda),
194         pattern_(move(pattern)),
195         expr_(move(expr)),
196         nslots_(nslots)
197     {}
198 
199     /// Print a value like a Curv expression.
200     virtual void print_repr(std::ostream&) const;
201 };
202 
203 /// A user-defined function value,
204 /// represented by a closure over a lambda expression.
205 struct Closure : public Function
206 {
207     Shared<const Pattern> pattern_;
208     Shared<Operation> expr_;
209     Shared<Module> nonlocals_;
210 
ClosureClosure211     Closure(
212         Shared<const Pattern> pattern,
213         Shared<Operation> expr,
214         Shared<Module> nonlocals,
215         slot_t nslots)
216     :
217         Function(nslots),
218         pattern_(move(pattern)),
219         expr_(move(expr)),
220         nonlocals_(move(nonlocals))
221     {}
222 
ClosureClosure223     Closure(
224         Lambda& lambda,
225         const Module& nonlocals)
226     :
227         Function(lambda.nslots_),
228         pattern_(lambda.pattern_),
229         expr_(lambda.expr_),
230         nonlocals_(share(const_cast<Module&>(nonlocals)))
231     {
232         name_ = lambda.name_;
233         argpos_ = lambda.argpos_;
234     }
235 
236     void print_help(std::ostream&) const override;
237 
238     virtual Value call(Value, Fail, Frame&) const override;
239     virtual void tail_call(Value, std::unique_ptr<Frame>&) const override;
240     virtual bool try_tail_call(Value, std::unique_ptr<Frame>&) const override;
241 
242     // generate a call to the function during SubCurv compilation
243     virtual SC_Value sc_call_expr(Operation&, Shared<const Phrase>, SC_Frame&) const override;
244 };
245 
246 struct Piecewise_Function : public Function
247 {
248     std::vector<Shared<const Function>> cases_;
249 
250     static slot_t maxslots(std::vector<Shared<const Function>>&);
251 
Piecewise_FunctionPiecewise_Function252     Piecewise_Function(std::vector<Shared<const Function>> cases)
253     :
254         Function(maxslots(cases)),
255         cases_(move(cases))
256     {}
257 
258     void print_help(std::ostream&) const override;
259 
260     // call the function during evaluation, with specified argument value.
261     virtual Value call(Value, Fail, Frame&) const override;
262     virtual void tail_call(Value, std::unique_ptr<Frame>&) const override;
263     virtual bool try_tail_call(Value, std::unique_ptr<Frame>&) const override;
264 
265     // generate a call to the function during SubCurv compilation
266     virtual SC_Value sc_call_expr(Operation&, Shared<const Phrase>, SC_Frame&) const override;
267 };
268 
269 struct Composite_Function : public Function
270 {
271     std::vector<Shared<const Function>> cases_;
272 
273     static slot_t maxslots(std::vector<Shared<const Function>>&);
274 
Composite_FunctionComposite_Function275     Composite_Function(std::vector<Shared<const Function>> cases)
276     :
277         Function(maxslots(cases)),
278         cases_(move(cases))
279     {}
280 
281     // call the function during evaluation, with specified argument value.
282     virtual Value call(Value, Fail, Frame&) const override;
283 
284     // generate a call to the function during SubCurv compilation
285     virtual SC_Value sc_call_expr(Operation&, Shared<const Phrase>, SC_Frame&)
286         const override;
287 };
288 
289 } // namespace curv
290 #endif // header guard
291