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