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_CONTEXT_H
6 #define LIBCURV_CONTEXT_H
7 
8 #include <list>
9 #include <libcurv/func_loc.h>
10 #include <libcurv/frame.h>
11 
12 namespace curv {
13 
14 struct Phrase;
15 struct Environ;
16 struct Scanner;
17 struct System;
18 
19 /// The primary use of a Context is to describe the source code Location and
20 /// call stack of a compile-time or run time error. A Context reference is
21 /// passed as the first constructor argument to curv::Exception, which is used
22 /// for reporting errors and warnings. The Context is used to construct the
23 /// stack trace, via Context::get_locations().
24 ///
25 /// Libcurv is written in a functional style. There are no global mutable
26 /// variables. Runtime functions in libcurv have no access to 'global'
27 /// interpreter state, exception via arguments that are passed in. Since you
28 /// need a Context to throw an exception, many runtime functions
29 /// have a Context argument, if they don't already have a Frame argument.
30 ///
31 /// As a consequence of this coding style, the secondary use of a Context is
32 /// to gain access to the System object, and to the Frame at the top of the
33 /// evaluator stack, via Context::system() and Context::frame().
34 /// This is needed to construct a Program object for compiling a Curv source
35 /// file. This need can arise whenever the `lib` is passed around as a value,
36 /// due to lazy evaluation and lazy program loading in `lib`.
37 ///
38 /// We only need Context::frame() at runtime: that's when `lib` expressions
39 /// are evaluated. During SC_Compile, Context::frame() returns nullptr.
40 ///
41 /// Context objects must be very cheap to construct.
42 /// Therefore, they are small, with only a few fields to fill in, and with
43 /// little or no computation required in the constructor. There are many
44 /// Context subclasses, each tailored to a particular kind of context, where
45 /// different compile-time or run-time data is available. In each case, we
46 /// take whatever data structures are readily available, store references to
47 /// them in the Context object, and later (if an Exception is thrown),
48 /// then we perform additional and more expensive computation to construct
49 /// a stack trace. So construction is cheap, but Context::get_location() is
50 /// expensive.
51 ///
52 /// Context objects are not allocated on the heap, and don't own the data
53 /// structures that they point to. Instances of Context subclasses are usually
54 /// constructed in argument position as rvalues, and passed as `const Context&`
55 /// parameters to functions that may throw a curv::Exception.
56 ///
57 /// Context::frame() and Context::system() are a little more expensive than
58 /// the Context constructors, but that's okay, because we need them when
59 /// loading a source file into memory, which is already expensive.
60 struct Context
61 {
~ContextContext62     virtual ~Context() {}
63     virtual void get_locations(std::list<Func_Loc>&) const = 0;
64     virtual Shared<const String> rewrite_message(Shared<const String>) const;
65     virtual System& system() const = 0;
66     virtual Frame* frame() const = 0;
67 };
68 
69 void get_frame_locations(const Frame* f, std::list<Func_Loc>& locs);
70 
71 struct At_System : public Context
72 {
73     System& system_;
74 
At_SystemAt_System75     At_System(System& system) : system_(system) {}
76 
77     virtual void get_locations(std::list<Func_Loc>& locs) const override;
78     virtual System& system() const override;
79     virtual Frame* frame() const override;
80 };
81 
82 struct At_SState : public Context
83 {
84     Source_State& sstate_;
85 
At_SStateAt_SState86     At_SState(Source_State& ss) : sstate_(ss) {}
87 
88     virtual void get_locations(std::list<Func_Loc>& locs) const override;
89     virtual System& system() const override;
90     virtual Frame* frame() const override;
91 };
92 
93 struct At_Token : public Context
94 {
95     Src_Loc loc_;
96     System& system_;
97     Frame* file_frame_;
98 
99     At_Token(Token, const Scanner&);
100     At_Token(Token, const Phrase&, Environ&);
101     At_Token(Token, const Phrase&, Frame&);
102     At_Token(Src_Loc, Environ&);
103     At_Token(Src_Loc, System&, Frame* = nullptr);
104 
105     virtual void get_locations(std::list<Func_Loc>&) const override;
106     virtual System& system() const override;
107     virtual Frame* frame() const override;
108 };
109 
110 // An abstract Context class which contains a const Phrase& reference,
111 // indicating the span of program text where the error occurred.
112 struct At_Syntax : public Context
113 {
114     virtual const Phrase& syntax() const = 0;
115 };
116 
117 struct At_Frame : public At_Syntax
118 {
119     Frame& call_frame_;
120 
At_FrameAt_Frame121     At_Frame(Frame& frame) : call_frame_(frame) {}
122 
123     virtual void get_locations(std::list<Func_Loc>& locs) const override;
124     virtual System& system() const override;
125     virtual Frame* frame() const override;
126     virtual const Phrase& syntax() const override;
127 };
128 
129 /// Exception Context where we know the Phrase that contains the error.
130 struct At_Phrase : public At_Syntax
131 {
132     Shared<const Phrase> phrase_;
133     System& system_;
134     Frame* frame_; // file_frame or call_frame
135 
136     At_Phrase(Shared<const Phrase> phrase, Frame& call_frame);
137     At_Phrase(const Phrase& phrase, Frame& call_frame);
138     At_Phrase(const Phrase& phrase, Source_State&);
139     At_Phrase(const Phrase& phrase, Scanner& scanner);
140     At_Phrase(const Phrase& phrase, Environ& env);
141 
142     virtual void get_locations(std::list<Func_Loc>& locs) const override;
143     virtual System& system() const override;
144     virtual Frame* frame() const override;
145     virtual const Phrase& syntax() const override;
146 };
147 
148 struct At_Program : public At_Syntax
149 {
150     Source_State& sstate_;
151     const Phrase& syntax_;
152 
153     // works with curv::Program, curv::Shape_Program, curv::GPU_Program
154     template <class PROGRAM>
At_ProgramAt_Program155     explicit At_Program(PROGRAM& prog)
156     :
157         sstate_(prog.sstate_), syntax_(prog.syntax())
158     {}
159 
160     virtual void get_locations(std::list<Func_Loc>& locs) const override;
161     virtual System& system() const override;
162     virtual Frame* frame() const override;
163     virtual const Phrase& syntax() const override;
164 };
165 
166 // Bad argument to a function call.
167 // The Frame argument is the stack frame for the function call.
168 struct At_Arg : public At_Syntax
169 {
170     const Function& func_;
171     Frame& call_frame_;
172 
At_ArgAt_Arg173     At_Arg(const Function& fn, Frame& fm)
174     :
175         func_(fn),
176         call_frame_(fm)
177     {}
178 
179     void get_locations(std::list<Func_Loc>& locs) const override;
180     virtual Shared<const String> rewrite_message(Shared<const String>) const override;
181     virtual System& system() const override;
182     virtual Frame* frame() const override;
183     virtual const Phrase& syntax() const override;
184 };
185 
186 // Bad argument to a metafunction call (Exception Context only).
187 // The Frame argument is the parent stack frame; the metafunction call doesn't
188 // have its own stack frame. This is good if the Context is just being used as
189 // an Exception context, but not good for more general uses, such as passing
190 // to an import function, where the frame() is accessed. For a more expensive
191 // but more general purpose Context, use At_Metacall_With_Call_Frame.
192 struct At_Metacall : public At_Syntax
193 {
194     const char* name_;
195     unsigned argpos_;
196     const Phrase& arg_;
197     Frame& parent_frame_;
198 
At_MetacallAt_Metacall199     At_Metacall(const char* name, unsigned argpos, const Phrase& arg, Frame& fm)
200     :
201         name_(name),
202         argpos_(argpos),
203         arg_(arg),
204         parent_frame_(fm)
205     {}
206 
207     void get_locations(std::list<Func_Loc>& locs) const override;
208     virtual Shared<const String> rewrite_message(Shared<const String>) const override;
209     virtual System& system() const override;
210     virtual Frame* frame() const override;
211     virtual const Phrase& syntax() const override;
212 };
213 
214 // Bad argument to a metafunction call (general purpose Context).
215 // The client must explicitly allocate a call frame for the metafunction call,
216 // and pass it as the frame argument.
217 struct At_Metacall_With_Call_Frame : public At_Syntax
218 {
219     const char* name_;
220     unsigned argpos_;
221     Frame& call_frame_;
222 
At_Metacall_With_Call_FrameAt_Metacall_With_Call_Frame223     At_Metacall_With_Call_Frame(const char* name, unsigned argpos, Frame& fm)
224     :
225         name_(name),
226         argpos_(argpos),
227         call_frame_(fm)
228     {}
229 
230     void get_locations(std::list<Func_Loc>& locs) const override;
231     virtual Shared<const String> rewrite_message(Shared<const String>) const override;
232     virtual System& system() const override;
233     virtual Frame* frame() const override;
234     virtual const Phrase& syntax() const override;
235 };
236 
237 // A convenience class, to automate the boilerplate of deriving from At_Syntax.
238 struct At_Syntax_Wrapper : public At_Syntax
239 {
240     const At_Syntax& parent_;
At_Syntax_WrapperAt_Syntax_Wrapper241     At_Syntax_Wrapper(const At_Syntax& cx) : parent_(cx) {}
242     Shared<const String> rewrite_message(Shared<const String> s) const override;
243     void get_locations(std::list<Func_Loc>& locs) const override;
244     System& system() const override;
245     Frame* frame() const override;
246     const Phrase& syntax() const override;
247 };
248 
249 struct At_Field_Syntax : public At_Syntax_Wrapper
250 {
251     const char* fieldname_;
252 
253     At_Field_Syntax(const char* fieldname, const At_Syntax& parent);
254 
255     virtual Shared<const String> rewrite_message(Shared<const String>) const override;
256 };
257 
258 struct At_Index_Syntax : public At_Syntax_Wrapper
259 {
260     size_t index_;
261 
262     At_Index_Syntax(size_t index, const At_Syntax& parent);
263 
264     virtual Shared<const String> rewrite_message(Shared<const String>) const override;
265 };
266 
267 struct At_Field : public Context
268 {
269     const char* fieldname_;
270     const Context& parent_;
271 
272     At_Field(const char* fieldname, const Context& parent);
273 
274     virtual void get_locations(std::list<Func_Loc>&) const override;
275     virtual Shared<const String> rewrite_message(Shared<const String>) const override;
276     virtual System& system() const override;
277     virtual Frame* frame() const override;
278 };
279 
280 struct At_Index : public Context
281 {
282     size_t index_;
283     const Context& parent_;
284 
285     At_Index(size_t index, const Context& parent);
286 
287     virtual void get_locations(std::list<Func_Loc>&) const override;
288     virtual Shared<const String> rewrite_message(Shared<const String>) const override;
289     virtual System& system() const override;
290     virtual Frame* frame() const override;
291 };
292 
293 } // namespace curv
294 #endif // header guard
295