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