| 1 | // Target Language Constructs
 | 
| 2 | //
 | 
| 3 | // We're generating a subset of C++.
 | 
| 4 | //
 | 
| 5 | // - Done:
 | 
| 6 | //   - initializer lists
 | 
| 7 | //   - exceptions
 | 
| 8 | //   - default arguments
 | 
| 9 | //   - namespaces
 | 
| 10 | //
 | 
| 11 | // - advanced:
 | 
| 12 | //   - What do Python closures get translated to?  Oil uses them in a few
 | 
| 13 | //     places, e.g. for the readline callbacks.
 | 
| 14 | //   - C++ 20 coroutines (but we're almost certainly not using this)
 | 
| 15 | 
 | 
| 16 | #include <sys/mman.h>  // mmap()
 | 
| 17 | 
 | 
| 18 | #include <initializer_list>
 | 
| 19 | #include <memory>  // shared_ptr
 | 
| 20 | #include <stdexcept>
 | 
| 21 | #include <unordered_map>
 | 
| 22 | #include <vector>
 | 
| 23 | 
 | 
| 24 | #include "mycpp/runtime.h"
 | 
| 25 | #include "vendor/greatest.h"
 | 
| 26 | 
 | 
| 27 | using std::unordered_map;
 | 
| 28 | 
 | 
| 29 | class RootingScope2 {
 | 
| 30 |  public:
 | 
| 31 |   RootingScope2() {
 | 
| 32 |   }
 | 
| 33 |   RootingScope2(const char* func_name) {
 | 
| 34 |     log(">>> %s", func_name);
 | 
| 35 |   }
 | 
| 36 |   ~RootingScope2() {
 | 
| 37 |   }
 | 
| 38 | };
 | 
| 39 | 
 | 
| 40 | #define ROOTING_REPORT 1
 | 
| 41 | 
 | 
| 42 | #if ROOTING_REPORT
 | 
| 43 |   #define FUNC_NAME_2() __PRETTY_FUNCTION__
 | 
| 44 | #else
 | 
| 45 |   #define FUNC_NAME_2()
 | 
| 46 | #endif
 | 
| 47 | 
 | 
| 48 | class MyList {
 | 
| 49 |  public:
 | 
| 50 |   MyList(std::initializer_list<int> init) : v_() {
 | 
| 51 |     for (int i : init) {
 | 
| 52 |       v_.push_back(i);
 | 
| 53 |     }
 | 
| 54 |   }
 | 
| 55 |   std::vector<int> v_;
 | 
| 56 | };
 | 
| 57 | 
 | 
| 58 | template <class T>
 | 
| 59 | class Array {
 | 
| 60 |  public:
 | 
| 61 |   Array() : v_() {
 | 
| 62 |   }
 | 
| 63 | 
 | 
| 64 |   Array(std::initializer_list<T> init) : v_() {
 | 
| 65 |     for (T i : init) {
 | 
| 66 |       v_.push_back(i);
 | 
| 67 |     }
 | 
| 68 |   }
 | 
| 69 | 
 | 
| 70 |   void append(T item) {
 | 
| 71 |     RootingScope2 _r(FUNC_NAME_2());
 | 
| 72 | 
 | 
| 73 |     v_.push_back(item);
 | 
| 74 |   }
 | 
| 75 | 
 | 
| 76 |   int size() {
 | 
| 77 |     return v_.size();
 | 
| 78 |   }
 | 
| 79 | 
 | 
| 80 |   std::vector<T> v_;
 | 
| 81 | };
 | 
| 82 | 
 | 
| 83 | class FatalError {};
 | 
| 84 | 
 | 
| 85 | class ParseError : public FatalError {
 | 
| 86 |  public:
 | 
| 87 |   ParseError(const char* reason) : reason_(reason) {
 | 
| 88 |   }
 | 
| 89 |   const char* reason() const {
 | 
| 90 |     RootingScope2 _r(FUNC_NAME_2());
 | 
| 91 | 
 | 
| 92 |     return reason_;
 | 
| 93 |   }
 | 
| 94 | 
 | 
| 95 |  private:
 | 
| 96 |   const char* reason_;
 | 
| 97 | };
 | 
| 98 | 
 | 
| 99 | // https://stackoverflow.com/questions/8480640/how-to-throw-a-c-exception
 | 
| 100 | int compare(int a, int b) {
 | 
| 101 |   if (a < 0 || b < 0) {
 | 
| 102 |     throw std::invalid_argument("received negative value");
 | 
| 103 |   }
 | 
| 104 |   return a < b;
 | 
| 105 | }
 | 
| 106 | 
 | 
| 107 | int parse(const char* text) {
 | 
| 108 |   if (text[0] == 'f') {
 | 
| 109 |     throw ParseError("started with f");
 | 
| 110 |   }
 | 
| 111 |   return 0;
 | 
| 112 | }
 | 
| 113 | 
 | 
| 114 | void throw_fatal() {
 | 
| 115 |   throw FatalError();
 | 
| 116 | }
 | 
| 117 | 
 | 
| 118 | void except_subclass_demo() {
 | 
| 119 |   try {
 | 
| 120 |     throw_fatal();
 | 
| 121 |     // parse("f");
 | 
| 122 |   } catch (ParseError& e) {
 | 
| 123 |     // Doesn't get caught.  Does this rely on RTTI, or is it static?
 | 
| 124 |     // I think it's static but increases the size of the exception table.
 | 
| 125 |     log("Got ParseError: %s", e.reason());
 | 
| 126 |   }
 | 
| 127 | }
 | 
| 128 | 
 | 
| 129 | TEST except_demo() {
 | 
| 130 |   int num_caught = 0;
 | 
| 131 | 
 | 
| 132 |   log("compare(3, 1): %d", compare(1, 3));
 | 
| 133 |   log("compare(5, 4): %d", compare(5, 4));
 | 
| 134 | 
 | 
| 135 |   try {
 | 
| 136 |     log("compare(-1, 3): %d", compare(-1, 3));
 | 
| 137 |   } catch (const std::invalid_argument& e) {
 | 
| 138 |     log("Got exception: %s", e.what());
 | 
| 139 |     num_caught++;
 | 
| 140 |   }
 | 
| 141 | 
 | 
| 142 |   log("");
 | 
| 143 | 
 | 
| 144 |   try {
 | 
| 145 |     log("parse('foo'): %d", parse("foo"));
 | 
| 146 |   } catch (const ParseError& e) {
 | 
| 147 |     log("Got exception: %s", e.reason());
 | 
| 148 |     num_caught++;
 | 
| 149 |   }
 | 
| 150 | 
 | 
| 151 |   try {
 | 
| 152 |     log("parse('bar'): %d", parse("bar"));
 | 
| 153 |   } catch (const ParseError& e) {
 | 
| 154 |     log("Got exception: %s", e.reason());
 | 
| 155 |     num_caught++;  // we don't get here
 | 
| 156 |   }
 | 
| 157 | 
 | 
| 158 |   try {
 | 
| 159 |     except_subclass_demo();
 | 
| 160 |   } catch (const FatalError& e) {
 | 
| 161 |     log("Got FatalError");
 | 
| 162 |     num_caught++;
 | 
| 163 |   }
 | 
| 164 | 
 | 
| 165 |   ASSERT_EQ_FMT(3, num_caught, "%d");
 | 
| 166 | 
 | 
| 167 |   PASS();
 | 
| 168 | }
 | 
| 169 | 
 | 
| 170 | TEST template_demo() {
 | 
| 171 |   Array<int> a;
 | 
| 172 |   a.append(1);
 | 
| 173 |   a.append(2);
 | 
| 174 |   a.append(3);
 | 
| 175 |   log("a.size() = %d", a.size());
 | 
| 176 | 
 | 
| 177 |   Array<MyList*> a2;
 | 
| 178 |   a2.append(new MyList{1, 2, 3});
 | 
| 179 |   a2.append(new MyList{4, 5, 6});
 | 
| 180 |   log("a2.size() = %d", a2.size());
 | 
| 181 | 
 | 
| 182 |   PASS();
 | 
| 183 | }
 | 
| 184 | 
 | 
| 185 | // prototype
 | 
| 186 | void f(int a, int b = -1, const char* s = nullptr);
 | 
| 187 | 
 | 
| 188 | void f(int a, int b, const char* s) {
 | 
| 189 |   log("");
 | 
| 190 |   log("a = %d", a);
 | 
| 191 |   log("b = %d", b);
 | 
| 192 |   log("s = %p", s);
 | 
| 193 | }
 | 
| 194 | 
 | 
| 195 | class Foo {
 | 
| 196 |  public:
 | 
| 197 |   // Is there any downside to these default args?
 | 
| 198 |   // Only for virtual functions.  Note that they are re-evaluated at each call
 | 
| 199 |   // site, which is fine.
 | 
| 200 |   //
 | 
| 201 |   // https://google.github.io/styleguide/cppguide.html#Default_Arguments
 | 
| 202 |   Foo(int i, bool always_strict = false);
 | 
| 203 | 
 | 
| 204 |   void Print() {
 | 
| 205 |     log("i = %d", i);
 | 
| 206 |     log("always_strict = %d", always_strict);
 | 
| 207 |   }
 | 
| 208 | 
 | 
| 209 |   int i;
 | 
| 210 |   bool always_strict;
 | 
| 211 | };
 | 
| 212 | 
 | 
| 213 | Foo::Foo(int i, bool always_strict) : i(i), always_strict(always_strict) {
 | 
| 214 | }
 | 
| 215 | 
 | 
| 216 | TEST default_args_demo() {
 | 
| 217 |   f(42, 43, "foo");
 | 
| 218 |   f(42, 43);
 | 
| 219 |   f(42);
 | 
| 220 | 
 | 
| 221 |   Foo a(98);
 | 
| 222 |   a.Print();
 | 
| 223 |   Foo b(99, true);
 | 
| 224 |   b.Print();
 | 
| 225 | 
 | 
| 226 |   PASS();
 | 
| 227 | }
 | 
| 228 | 
 | 
| 229 | namespace core {
 | 
| 230 | namespace util {
 | 
| 231 | void p_die(const char* s) {
 | 
| 232 |   log("p_die %s", s);
 | 
| 233 | }
 | 
| 234 | }  // namespace util
 | 
| 235 | }  // namespace core
 | 
| 236 | 
 | 
| 237 | namespace tdop {
 | 
| 238 | using core::util::p_die;
 | 
| 239 | 
 | 
| 240 | class Parser {
 | 
| 241 |  public:
 | 
| 242 |   Parser(int token) : token_(token) {
 | 
| 243 |     log("Parser %d", token);
 | 
| 244 |     p_die("Parser");
 | 
| 245 |   }
 | 
| 246 |   int token_;
 | 
| 247 | };
 | 
| 248 | }  // namespace tdop
 | 
| 249 | 
 | 
| 250 | namespace typed_arith_parse {
 | 
| 251 | // using namespace core;  This makes EVERYTHING available.
 | 
| 252 | 
 | 
| 253 | namespace util = core::util;
 | 
| 254 | 
 | 
| 255 | // This lets us use "Parser""
 | 
| 256 | using tdop::Parser;
 | 
| 257 | 
 | 
| 258 | TEST namespace_demo() {
 | 
| 259 |   log("");
 | 
| 260 |   log("namespace_demo()");
 | 
| 261 |   f(42);
 | 
| 262 |   auto unused1 = new tdop::Parser(42);
 | 
| 263 |   auto unused2 = new Parser(43);
 | 
| 264 |   (void)unused1;
 | 
| 265 |   (void)unused2;
 | 
| 266 | 
 | 
| 267 |   util::p_die("ns");
 | 
| 268 | 
 | 
| 269 |   PASS();
 | 
| 270 | }
 | 
| 271 | }  // namespace typed_arith_parse
 | 
| 272 | 
 | 
| 273 | // Conclusion: every Python module should have is own namespace
 | 
| 274 | //
 | 
| 275 | // from core.util import log => using core::util::log
 | 
| 276 | // from core import util => namespace util = core::util;
 | 
| 277 | 
 | 
| 278 | // test out the size of 5 uint16_t.  OK it's actually padded, which is nice!
 | 
| 279 | // Because there is no big element.
 | 
| 280 | struct Extent {
 | 
| 281 |   uint16_t s_line_id;
 | 
| 282 |   uint16_t s_col;
 | 
| 283 |   uint16_t e_line_id;
 | 
| 284 |   uint16_t e_col;
 | 
| 285 |   uint16_t src_id;
 | 
| 286 | };
 | 
| 287 | 
 | 
| 288 | class expr__Const {
 | 
| 289 |  public:
 | 
| 290 |   expr__Const(int i) : i_(i) {
 | 
| 291 |   }
 | 
| 292 |   int i_;
 | 
| 293 | };
 | 
| 294 | 
 | 
| 295 | namespace expr {
 | 
| 296 | typedef expr__Const Const;
 | 
| 297 | }
 | 
| 298 | 
 | 
| 299 | using std::make_shared;
 | 
| 300 | using std::shared_ptr;
 | 
| 301 | 
 | 
| 302 | shared_ptr<expr__Const> f(shared_ptr<expr__Const> arg) {
 | 
| 303 |   log("arg.use_count() = %d", arg.use_count());
 | 
| 304 |   return shared_ptr<expr__Const>(new expr__Const(arg->i_ + 10));
 | 
| 305 | }
 | 
| 306 | 
 | 
| 307 | TEST shared_ptr_demo() {
 | 
| 308 |   std::shared_ptr<expr__Const> e = make_shared<expr__Const>(5);
 | 
| 309 |   log("e->i_ = %d", e->i_);
 | 
| 310 |   log("e.use_count() = %d", e.use_count());
 | 
| 311 | 
 | 
| 312 |   // 16, not 24?
 | 
| 313 |   // These are contiguous.
 | 
| 314 |   log("sizeof(e) = %zu", sizeof(e));
 | 
| 315 |   log("");
 | 
| 316 | 
 | 
| 317 |   std::shared_ptr<expr__Const> e2(new expr__Const(7));
 | 
| 318 |   log("e2->i_ = %d", e2->i_);
 | 
| 319 |   log("e2.use_count() = %d", e2.use_count());
 | 
| 320 |   log("sizeof(e2) = %zu", sizeof(e2));
 | 
| 321 |   log("");
 | 
| 322 | 
 | 
| 323 |   std::shared_ptr<expr__Const> e3 = f(e2);
 | 
| 324 | 
 | 
| 325 |   log("e3->i_ = %d", e3->i_);
 | 
| 326 |   log("e3.use_count() = %d", e3.use_count());
 | 
| 327 |   log("sizeof(e3) = %zu", sizeof(e3));
 | 
| 328 |   log("");
 | 
| 329 | 
 | 
| 330 |   PASS();
 | 
| 331 | }
 | 
| 332 | 
 | 
| 333 | TEST map_demo() {
 | 
| 334 |   unordered_map<int, int> m;
 | 
| 335 |   log("m.size = %d", m.size());
 | 
| 336 | 
 | 
| 337 |   // Hm integers have a hash function
 | 
| 338 |   m[3] = 4;
 | 
| 339 |   m[5] = 9;
 | 
| 340 |   log("m.size = %d", m.size());
 | 
| 341 | 
 | 
| 342 |   // Hm you always get the pairs
 | 
| 343 |   // Should this be const auto& or something?
 | 
| 344 |   for (auto item : m) {
 | 
| 345 |     log("iterating %d %d", item.first, item.second);
 | 
| 346 |   }
 | 
| 347 | 
 | 
| 348 |   log("---");
 | 
| 349 | 
 | 
| 350 |   unordered_map<Extent*, int> m2;
 | 
| 351 |   log("m2.size = %d", m2.size());
 | 
| 352 | 
 | 
| 353 |   // hm do I want this operator overloading?
 | 
| 354 |   m2[nullptr] = 42;
 | 
| 355 |   log("m2.size = %d", m2.size());
 | 
| 356 | 
 | 
| 357 |   log("retrieved = %d", m2[nullptr]);
 | 
| 358 | 
 | 
| 359 |   PASS();
 | 
| 360 | }
 | 
| 361 | 
 | 
| 362 | TEST sizeof_demo() {
 | 
| 363 |   log("sizeof(int): %d", sizeof(int));
 | 
| 364 |   log("sizeof(int*): %d", sizeof(int*));
 | 
| 365 |   log("sizeof(Extent): %d", sizeof(Extent));
 | 
| 366 |   log("");
 | 
| 367 | 
 | 
| 368 |   // Good, this is 50.
 | 
| 369 |   Extent ext_array[5];
 | 
| 370 |   log("sizeof(ext_array): %d", sizeof(ext_array));
 | 
| 371 | 
 | 
| 372 |   PASS();
 | 
| 373 | }
 | 
| 374 | 
 | 
| 375 | TEST test_misc() {
 | 
| 376 |   MyList l{1, 2, 3};
 | 
| 377 |   log("size: %d", l.v_.size());
 | 
| 378 |   log("");
 | 
| 379 | 
 | 
| 380 |   // Dict literal syntax?
 | 
| 381 |   // Dict d {{"key", 1}, {"val", 2}};
 | 
| 382 | 
 | 
| 383 |   log("");
 | 
| 384 |   expr::Const c(42);
 | 
| 385 |   log("expr::Const = %d", c.i_);
 | 
| 386 | 
 | 
| 387 |   // dumb_alloc::Summarize();
 | 
| 388 | 
 | 
| 389 |   PASS();
 | 
| 390 | }
 | 
| 391 | 
 | 
| 392 | struct Point {
 | 
| 393 |   int x;
 | 
| 394 |   int y;
 | 
| 395 | };
 | 
| 396 | 
 | 
| 397 | // structs don't have any constructors, so don't need any constexpr stuff
 | 
| 398 | constexpr Point p = {3, 4};
 | 
| 399 | 
 | 
| 400 | // members must be public to allow initializer list
 | 
| 401 | class PointC {
 | 
| 402 |  public:
 | 
| 403 |   // constructor is allowed
 | 
| 404 |   // needs to be constexpr
 | 
| 405 |   constexpr PointC(int x, int y) : x_(x), y_(y) {
 | 
| 406 |   }
 | 
| 407 |   // this is allowed too
 | 
| 408 |   int get_x() {
 | 
| 409 |     return x_;
 | 
| 410 |   }
 | 
| 411 |   // this is allowed too
 | 
| 412 |   virtual int mag() const {
 | 
| 413 |     return x_ * x_ + y_ * y_;
 | 
| 414 |   }
 | 
| 415 | 
 | 
| 416 |   int x_;
 | 
| 417 |   int y_;
 | 
| 418 | };
 | 
| 419 | 
 | 
| 420 | constexpr PointC pc = {5, 6};
 | 
| 421 | 
 | 
| 422 | class SubPointC : public PointC {
 | 
| 423 |  public:
 | 
| 424 |   constexpr SubPointC(int x, int y) : PointC(x, y) {
 | 
| 425 |   }
 | 
| 426 |   virtual int mag() const {
 | 
| 427 |     return 0;
 | 
| 428 |   }
 | 
| 429 | };
 | 
| 430 | 
 | 
| 431 | constexpr SubPointC sub = {7, 8};
 | 
| 432 | 
 | 
| 433 | class Compound {
 | 
| 434 |  public:
 | 
| 435 |   PointC c1;
 | 
| 436 |   PointC c2;
 | 
| 437 | };
 | 
| 438 | 
 | 
| 439 | // This works, but what about pointers?
 | 
| 440 | constexpr Compound c = {{0, 1}, {8, 9}};
 | 
| 441 | 
 | 
| 442 | TEST static_literals() {
 | 
| 443 |   ASSERT_EQ(3, p.x);
 | 
| 444 |   ASSERT_EQ(4, p.y);
 | 
| 445 | 
 | 
| 446 |   ASSERT_EQ(5, pc.x_);
 | 
| 447 |   ASSERT_EQ(6, pc.y_);
 | 
| 448 | 
 | 
| 449 |   // I'm surprised virtual functions are allowed!  We're compiling with
 | 
| 450 |   // -std=c++11.
 | 
| 451 |   // But this is just curiosity.  We don't need this in ASDL.
 | 
| 452 |   ASSERT_EQ_FMT(61, pc.mag(), "%d");
 | 
| 453 | 
 | 
| 454 |   ASSERT_EQ_FMT(0, sub.mag(), "%d");
 | 
| 455 | 
 | 
| 456 |   ASSERT_EQ(0, c.c1.x_);
 | 
| 457 |   ASSERT_EQ(1, c.c1.y_);
 | 
| 458 |   ASSERT_EQ(8, c.c2.x_);
 | 
| 459 |   ASSERT_EQ(9, c.c2.y_);
 | 
| 460 | 
 | 
| 461 |   PASS();
 | 
| 462 | }
 | 
| 463 | 
 | 
| 464 | enum class Color_e { red, blue };
 | 
| 465 | 
 | 
| 466 | TEST enum_demo() {
 | 
| 467 |   Color_e c1 = Color_e::red;
 | 
| 468 |   Color_e c2 = Color_e::blue;
 | 
| 469 |   int array[2] = {3, 4};
 | 
| 470 | 
 | 
| 471 |   // You can cast these strong enums to an integer.  We don't do that in the
 | 
| 472 |   // MyPy source, but maybe we could?  It's kind of a pain though.
 | 
| 473 | 
 | 
| 474 |   log("c1 %d", static_cast<int>(c1));
 | 
| 475 |   log("c2 %d", static_cast<int>(c2));
 | 
| 476 | 
 | 
| 477 |   log("array[c1] %d", array[static_cast<int>(c1)]);
 | 
| 478 | 
 | 
| 479 |   PASS();
 | 
| 480 | }
 | 
| 481 | 
 | 
| 482 | class Node {
 | 
| 483 |  public:
 | 
| 484 |   int i;
 | 
| 485 |   int j;
 | 
| 486 |   Node* left;
 | 
| 487 |   int k;
 | 
| 488 |   // padding here on 64-bit, but not 32-bit
 | 
| 489 |   Node* right;
 | 
| 490 | };
 | 
| 491 | 
 | 
| 492 | #if 0
 | 
| 493 | constexpr uint16_t Node_mask() {
 | 
| 494 |   uint16_t mask = 0;
 | 
| 495 | 
 | 
| 496 |   constexpr int stride = sizeof(void*);
 | 
| 497 | 
 | 
| 498 |   constexpr int o1 = offsetof(Node, left);
 | 
| 499 |   static_assert(o1 % stride == 0, "oops");
 | 
| 500 | 
 | 
| 501 |   constexpr int o2 = offsetof(Node, right);
 | 
| 502 |   static_assert(o2 % stride == 0, "oops");
 | 
| 503 | 
 | 
| 504 |   constexpr int b1 = o1 / stride;
 | 
| 505 |   constexpr int b2 = o2 / stride;
 | 
| 506 | 
 | 
| 507 |   mask |= 1 << b1;
 | 
| 508 |   mask |= 1 << b2;
 | 
| 509 | 
 | 
| 510 |   return mask;
 | 
| 511 | }
 | 
| 512 | 
 | 
| 513 | #else
 | 
| 514 | 
 | 
| 515 | // C++ 11 version has to be a single expression!
 | 
| 516 | 
 | 
| 517 | constexpr uint16_t Node_mask() {
 | 
| 518 |   return (1 << (offsetof(Node, left) / sizeof(void*)) |
 | 
| 519 |           1 << (offsetof(Node, right) / sizeof(void*)));
 | 
| 520 | }
 | 
| 521 | 
 | 
| 522 | #endif
 | 
| 523 | 
 | 
| 524 | void print_bin(int n) {
 | 
| 525 |   for (int i = 15; i >= 0; --i) {
 | 
| 526 |     if (n & (1 << i))
 | 
| 527 |       putchar('1');
 | 
| 528 |     else
 | 
| 529 |       putchar('0');
 | 
| 530 |   }
 | 
| 531 |   putchar('\n');
 | 
| 532 | }
 | 
| 533 | 
 | 
| 534 | TEST field_mask_demo() {
 | 
| 535 |   int c1 = offsetof(Node, left);
 | 
| 536 |   int c2 = offsetof(Node, right);
 | 
| 537 |   log("c1 = %d, c2 = %d, sizeof(void*) = %d", c1, c2, sizeof(void*));
 | 
| 538 | 
 | 
| 539 |   log("Node_mask");
 | 
| 540 |   print_bin(Node_mask());
 | 
| 541 | 
 | 
| 542 |   PASS();
 | 
| 543 | }
 | 
| 544 | 
 | 
| 545 | class Base {
 | 
| 546 |  public:
 | 
| 547 |   Base(int i) : i(i) {
 | 
| 548 |   }
 | 
| 549 |   static constexpr ObjHeader obj_header() {
 | 
| 550 |     return ObjHeader::ClassFixed(kZeroMask, sizeof(Base));
 | 
| 551 |   }
 | 
| 552 |   int i;
 | 
| 553 |   Node* left;
 | 
| 554 |   Node* right;
 | 
| 555 | };
 | 
| 556 | 
 | 
| 557 | class Derived : public Base {
 | 
| 558 |  public:
 | 
| 559 |   Derived(int i, int j) : Base(i), j(j) {
 | 
| 560 |     // annoying: should be in initializer list
 | 
| 561 |     FIELD_MASK(*ObjHeader::FromObject(this)) |= 0x5;
 | 
| 562 |   }
 | 
| 563 |   int j;
 | 
| 564 |   Node* three;
 | 
| 565 | };
 | 
| 566 | 
 | 
| 567 | // Demonstrate problem with Local<T>
 | 
| 568 | #if 0
 | 
| 569 | TEST smartptr_inheritance_demo() {
 | 
| 570 |   Local<Base> b = Alloc<Base>(2);
 | 
| 571 |   Local<Derived> d = Alloc<Derived>(4, 5);
 | 
| 572 | 
 | 
| 573 |   ASSERT_EQ_FMT(2, b->i, "%d");
 | 
| 574 | 
 | 
| 575 |   ASSERT_EQ_FMT(4, d->i, "%d");
 | 
| 576 |   ASSERT_EQ_FMT(5, d->j, "%d");
 | 
| 577 | 
 | 
| 578 |   ASSERT_EQ_FMT(0x9, b->field_mask_, "%d");
 | 
| 579 |   ASSERT_EQ_FMT(0x5, d->field_mask_, "%d");
 | 
| 580 | 
 | 
| 581 |   PASS();
 | 
| 582 | }
 | 
| 583 | #endif
 | 
| 584 | 
 | 
| 585 | char* realloc(char* buf, size_t num_bytes) {
 | 
| 586 |   void* result = mmap(nullptr, num_bytes, PROT_READ | PROT_WRITE,
 | 
| 587 |                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 | 
| 588 |   memcpy(result, buf, num_bytes);
 | 
| 589 | 
 | 
| 590 |   // Now make it unreadable
 | 
| 591 |   int m = mprotect(buf, num_bytes, PROT_NONE);
 | 
| 592 |   log("mprotect = %d", m);
 | 
| 593 | 
 | 
| 594 |   return static_cast<char*>(result);
 | 
| 595 | }
 | 
| 596 | 
 | 
| 597 | TEST mmap_demo() {
 | 
| 598 |   size_t num_bytes = 1;
 | 
| 599 | 
 | 
| 600 |   void* tmp = mmap(nullptr, num_bytes, PROT_READ | PROT_WRITE,
 | 
| 601 |                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 | 
| 602 |   char* space = static_cast<char*>(tmp);
 | 
| 603 | 
 | 
| 604 |   *space = 42;
 | 
| 605 | 
 | 
| 606 |   log("space %p", space);
 | 
| 607 | 
 | 
| 608 |   log("value = %d", *space);
 | 
| 609 | 
 | 
| 610 |   space = realloc(space, num_bytes);
 | 
| 611 |   log("value = %d", *space);
 | 
| 612 | 
 | 
| 613 |   // Can't use this anymore
 | 
| 614 |   char* bad = static_cast<char*>(tmp);
 | 
| 615 |   (void)bad;
 | 
| 616 | 
 | 
| 617 |   PASS();
 | 
| 618 | }
 | 
| 619 | 
 | 
| 620 | TEST comma_demo() {
 | 
| 621 |   auto i = 3;
 | 
| 622 |   auto k = (i++, 5);
 | 
| 623 |   log("k = %d", k);
 | 
| 624 | 
 | 
| 625 |   auto n = new Node();
 | 
| 626 |   log("n = %p, n->i = %d, n->j = %d", n, n->i, n->j);
 | 
| 627 | 
 | 
| 628 |   // Hacky workaround ... actually this sorta works.  Gah.
 | 
| 629 |   Node* tmp;
 | 
| 630 |   auto n2 = (tmp = new Node(), tmp->i = 42, tmp);
 | 
| 631 |   log("n2 = %p, n2->i = %d, n2->j = %d", n2, n2->i, n2->j);
 | 
| 632 | 
 | 
| 633 |   PASS();
 | 
| 634 | }
 | 
| 635 | 
 | 
| 636 | // Trick here to print types at compile time
 | 
| 637 | //
 | 
| 638 | // https://stackoverflow.com/questions/60203857/print-a-types-name-at-compile-time-without-aborting-compilation
 | 
| 639 | 
 | 
| 640 | template <typename T>
 | 
| 641 | [[gnu::warning("your type here")]] bool print_type() {
 | 
| 642 |   return true;
 | 
| 643 | }
 | 
| 644 | 
 | 
| 645 | TEST signed_unsigned_demo() {
 | 
| 646 |   char c = '\xff';
 | 
| 647 |   log("c = %d", c);
 | 
| 648 |   log("c = %u", c);
 | 
| 649 |   log("c > 127 = %d", c > 127);             // FALSE because it's char
 | 
| 650 |   log("'\\xff' > 127 = %d", '\xff' > 127);  // also FALSE
 | 
| 651 | 
 | 
| 652 | #if 0
 | 
| 653 |   bool b1 = print_type<decltype(c)>();
 | 
| 654 | 
 | 
| 655 |   // The type of literal '\xff' is 'char'
 | 
| 656 |   bool b2 = print_type<decltype('\xff')>();
 | 
| 657 | 
 | 
| 658 |   log("b1 = %d", b1);
 | 
| 659 |   log("b2 = %d", b2);
 | 
| 660 | #endif
 | 
| 661 | 
 | 
| 662 |   PASS();
 | 
| 663 | }
 | 
| 664 | 
 | 
| 665 | class Object {
 | 
| 666 |  public:
 | 
| 667 |   uint32_t header;
 | 
| 668 | };
 | 
| 669 | 
 | 
| 670 | class Writer : public Object {
 | 
| 671 |  public:
 | 
| 672 |   // This vtable causes the quirk!
 | 
| 673 | #if 1
 | 
| 674 |   virtual int f() {
 | 
| 675 |     return 42;
 | 
| 676 |   }
 | 
| 677 | #endif
 | 
| 678 | };
 | 
| 679 | 
 | 
| 680 | void RootGlobalVar(Object* root) {
 | 
| 681 |   // Super weird behavior!!!  The param root is 8 bytes ahead of the argument
 | 
| 682 |   // gStdout!
 | 
| 683 |   log("root = %p", root);
 | 
| 684 | }
 | 
| 685 | 
 | 
| 686 | Writer* gStdout = nullptr;
 | 
| 687 | 
 | 
| 688 | Writer* Stdout() {
 | 
| 689 |   if (gStdout == nullptr) {
 | 
| 690 |     gStdout = new Writer();
 | 
| 691 |     log("gStdout = %p", gStdout);
 | 
| 692 | 
 | 
| 693 |     log("no cast");
 | 
| 694 |     RootGlobalVar(gStdout);
 | 
| 695 |     log("");
 | 
| 696 | 
 | 
| 697 |     log("reinterpret_cast");
 | 
| 698 |     RootGlobalVar(reinterpret_cast<Object*>(gStdout));
 | 
| 699 |     log("");
 | 
| 700 | 
 | 
| 701 |     log("static_cast");
 | 
| 702 |     RootGlobalVar(static_cast<Object*>(gStdout));
 | 
| 703 |     log("");
 | 
| 704 |   }
 | 
| 705 |   return gStdout;
 | 
| 706 | }
 | 
| 707 | 
 | 
| 708 | TEST param_passing_demo() {
 | 
| 709 |   Writer* writer = Stdout();
 | 
| 710 |   log("writer %p", writer);
 | 
| 711 |   log("");
 | 
| 712 | 
 | 
| 713 |   // Same behavior: surprising!
 | 
| 714 |   Object* obj = writer;
 | 
| 715 |   log("obj %p", obj);
 | 
| 716 |   log("");
 | 
| 717 | 
 | 
| 718 |   PASS();
 | 
| 719 | }
 | 
| 720 | 
 | 
| 721 | #define ENUM(name, schema)
 | 
| 722 | 
 | 
| 723 | #define SUM(name, ...)
 | 
| 724 | 
 | 
| 725 | #define VARIANT(...)
 | 
| 726 | 
 | 
| 727 | #define USE(path)
 | 
| 728 | 
 | 
| 729 | #define SUM_NS(name)
 | 
| 730 | 
 | 
| 731 | #define PROD(name) struct name
 | 
| 732 | 
 | 
| 733 | #define SCHEMA(name)
 | 
| 734 | 
 | 
| 735 | TEST tea_macros_demo() {
 | 
| 736 |   // The preprocessor does NOT expand this.  Instead we have a separate parser
 | 
| 737 |   // that does it.  Hm not bad.
 | 
| 738 |   //
 | 
| 739 |   // Problem: the processor has to expand imports.
 | 
| 740 | 
 | 
| 741 |   USE("frontend/syntax.asdl");
 | 
| 742 | 
 | 
| 743 |   // Without commas
 | 
| 744 | 
 | 
| 745 |   ENUM(
 | 
| 746 |       suffix_op,
 | 
| 747 | 
 | 
| 748 |       Nullary % Token |
 | 
| 749 |           Unary {
 | 
| 750 |             Token word;
 | 
| 751 |             Word arg_word
 | 
| 752 |           }
 | 
| 753 | 
 | 
| 754 |   );
 | 
| 755 | 
 | 
| 756 |   // More natural comma syntax.  Although less consistent with C++.
 | 
| 757 |   // TODO: See what clang-format does on these.
 | 
| 758 |   // Oh it treats:
 | 
| 759 |   // - % and | as binary operators
 | 
| 760 |   // - ; breaks a line but comma doesn't , which I might not want
 | 
| 761 |   //
 | 
| 762 |   // OK () and , looks better, but no line breaking.  Maybe there is a
 | 
| 763 |   // clang-format option.
 | 
| 764 |   //
 | 
| 765 |   // Enabled WhitespaceSensitiveMacros for now.
 | 
| 766 | 
 | 
| 767 |   SUM(suffix_op,
 | 
| 768 |       Nullary #Token,
 | 
| 769 |       Unary(Token word, Word arg_word),
 | 
| 770 |       Static(Token tok, BigStr arg)
 | 
| 771 |   );
 | 
| 772 | 
 | 
| 773 |   SUM(suffix_op,
 | 
| 774 | 
 | 
| 775 |       Nullary #Token;
 | 
| 776 |       Unary {
 | 
| 777 |         Token word;
 | 
| 778 |         Word arg_word;
 | 
| 779 |       }
 | 
| 780 |       Static {
 | 
| 781 |         Token tok;
 | 
| 782 |         BigStr arg;
 | 
| 783 |       }
 | 
| 784 |   );
 | 
| 785 | 
 | 
| 786 |   // The C++ compiler parses and validates these
 | 
| 787 |   // Problem: recursive types and so forth.  We would need forward declarations
 | 
| 788 |   // and all that?
 | 
| 789 |   // It's also a bit more verbose.
 | 
| 790 |   // How to do the % reference?  typedef?
 | 
| 791 | 
 | 
| 792 |   PROD(Token) {
 | 
| 793 |     int id;
 | 
| 794 |     BigStr val;
 | 
| 795 |   };
 | 
| 796 |   struct Word {};
 | 
| 797 | 
 | 
| 798 |   SUM_NS(suffix_op) {
 | 
| 799 |     // typedef Token Nullary;
 | 
| 800 |     struct Unary {
 | 
| 801 |       Token op;
 | 
| 802 |       Word arg_word;
 | 
| 803 |     };
 | 
| 804 |   }
 | 
| 805 | 
 | 
| 806 |   SCHEMA(
 | 
| 807 |     data Token(Id id, BigStr val);
 | 
| 808 | 
 | 
| 809 |     enum suffix_op {
 | 
| 810 |       Nullary %Token
 | 
| 811 |     | Unary(Token op, Word arg_word)
 | 
| 812 |     }
 | 
| 813 | 
 | 
| 814 |     // I guess we retain * for reference semantics and so forth
 | 
| 815 |     // *out = val; can be useful
 | 
| 816 | 
 | 
| 817 |     data Other(Word[] words, Dict<BigStr, Word>* mydict, BigStr? option);
 | 
| 818 | 
 | 
| 819 |     // List<Word>* is also possible, but a bit verbose
 | 
| 820 |     // Word words[] would be more like C++
 | 
| 821 |     //
 | 
| 822 |     // Probably want something more clearly different like:
 | 
| 823 |     //
 | 
| 824 |     // Word... words
 | 
| 825 |     // [Word] words   -- synonym for List<Word>* words
 | 
| 826 |     // Word@ words    -- not bad, for repetition
 | 
| 827 |     //
 | 
| 828 |     // There are also grammars with + and [] though
 | 
| 829 |   );
 | 
| 830 | 
 | 
| 831 |   printf("Sum types defined");
 | 
| 832 | 
 | 
| 833 |   PASS();
 | 
| 834 | }
 | 
| 835 | 
 | 
| 836 | // DEMO of putting PURE interfaces with CALLERS, like Go and TypeScript
 | 
| 837 | // structural types
 | 
| 838 | 
 | 
| 839 | // First package
 | 
| 840 | class Reader1 {
 | 
| 841 |  public:
 | 
| 842 |   virtual int Read(int n) = 0;
 | 
| 843 | };
 | 
| 844 | 
 | 
| 845 | // Second package
 | 
| 846 | class Reader2 {
 | 
| 847 |  public:
 | 
| 848 |   virtual int Read(int n) = 0;
 | 
| 849 | };
 | 
| 850 | 
 | 
| 851 | class Writer2 {
 | 
| 852 |  public:
 | 
| 853 |   virtual int Write(int n) = 0;
 | 
| 854 | };
 | 
| 855 | 
 | 
| 856 | // Tea could calculate implicit interfaces globally, and then emit these
 | 
| 857 | // explicit inheritance relationships
 | 
| 858 | 
 | 
| 859 | // Multiply inheriting from abstract methods seems fine!
 | 
| 860 | class Concrete : public Reader1, public Reader2, public Writer2 {
 | 
| 861 |  public:
 | 
| 862 |   virtual int Read(int n) {
 | 
| 863 |     printf("Concrete Read(%d)\n", n);
 | 
| 864 |     return 0;
 | 
| 865 |   }
 | 
| 866 | 
 | 
| 867 |   virtual int Write(int n) {
 | 
| 868 |     printf("Concrete Write(%d)\n", n);
 | 
| 869 |     return 0;
 | 
| 870 |   }
 | 
| 871 | };
 | 
| 872 | 
 | 
| 873 | class Concrete1 : public Reader1 {};
 | 
| 874 | 
 | 
| 875 | class Concrete2 : public Reader1, public Reader2 {};
 | 
| 876 | 
 | 
| 877 | /*
 | 
| 878 |  Would be something like
 | 
| 879 | 
 | 
| 880 |  interface Reader1 {
 | 
| 881 |    func Read(n Int) -> Int
 | 
| 882 |  }
 | 
| 883 |  interface Reader2 {
 | 
| 884 |    func Read(n Int) -> Int
 | 
| 885 |  }
 | 
| 886 |  class Concrete {
 | 
| 887 |    func Read(n Int) -> Int{
 | 
| 888 |      log("echo")
 | 
| 889 |      return 0
 | 
| 890 |    }
 | 
| 891 |  }
 | 
| 892 |  */
 | 
| 893 | 
 | 
| 894 | TEST tea_interface() {
 | 
| 895 |   Concrete val;
 | 
| 896 | 
 | 
| 897 |   // 8 bytes
 | 
| 898 |   log("sizeof(Concrete1) = %d", sizeof(Concrete1));
 | 
| 899 |   // 16 bytes
 | 
| 900 |   log("sizeof(Concrete2) = %d", sizeof(Concrete2));
 | 
| 901 | 
 | 
| 902 |   // 24 bytes because it has 3 vtables?
 | 
| 903 |   // Hm yes, this idea doesn't scale because objects become bloated with
 | 
| 904 |   // vtables.  Though interestingly 2 different vtables can point to the same
 | 
| 905 |   // concrete Read() method.
 | 
| 906 |   log("sizeof(Concrete) = %d", sizeof(Concrete));
 | 
| 907 |   log("sizeof(val) = %d", sizeof(val));
 | 
| 908 | 
 | 
| 909 |   Concrete* c = &val;
 | 
| 910 |   c->Read(3);
 | 
| 911 | 
 | 
| 912 |   Reader1* r1 = c;
 | 
| 913 |   r1->Read(4);
 | 
| 914 | 
 | 
| 915 |   Reader2* r2 = c;
 | 
| 916 |   r2->Read(5);
 | 
| 917 | 
 | 
| 918 |   Writer2* w = c;
 | 
| 919 |   w->Write(6);
 | 
| 920 | 
 | 
| 921 |   PASS();
 | 
| 922 | }
 | 
| 923 | 
 | 
| 924 | namespace runtime_asdl {
 | 
| 925 | 
 | 
| 926 | class lvalue_t {};
 | 
| 927 | 
 | 
| 928 | class lvalue__Named : public lvalue_t {};
 | 
| 929 | 
 | 
| 930 | class lvalue__Indexed : public lvalue_t {};
 | 
| 931 | 
 | 
| 932 | #if 0
 | 
| 933 | namespace lvalue {
 | 
| 934 |   typedef lvalue__Named Named;
 | 
| 935 |   typedef lvalue__Indexed Indexed;
 | 
| 936 | }
 | 
| 937 | #endif
 | 
| 938 | 
 | 
| 939 | // A CLASS can substitute for a namespace, but it can be "imported" with "using"
 | 
| 940 | struct lvalue {
 | 
| 941 | #if 0
 | 
| 942 |   class Named: public lvalue_t {
 | 
| 943 |   };
 | 
| 944 |   class Indexed: public lvalue_t {
 | 
| 945 |   };
 | 
| 946 | #endif
 | 
| 947 | 
 | 
| 948 |   // typedef lvalue__Named Named;
 | 
| 949 |   // typedef lvalue__Indexed Indexed;
 | 
| 950 |   using Named = lvalue__Named;
 | 
| 951 |   using Indexed = lvalue__Indexed;
 | 
| 952 | };
 | 
| 953 | 
 | 
| 954 | };  // namespace runtime_asdl
 | 
| 955 | 
 | 
| 956 | namespace hnode_asdl {
 | 
| 957 | #if 0
 | 
| 958 | namespace hnode_e {
 | 
| 959 |   const int Record = 1;
 | 
| 960 |   const int Array = 2;
 | 
| 961 |   const int Leaf = 3;
 | 
| 962 |   const int External = 4;
 | 
| 963 | };
 | 
| 964 | #endif
 | 
| 965 | 
 | 
| 966 | // Not enum class, a namespace
 | 
| 967 | struct hnode_e {
 | 
| 968 | #if 0
 | 
| 969 |   static const int Record = 1;
 | 
| 970 |   static const int Array = 2;
 | 
| 971 |   static const int Leaf = 3;
 | 
| 972 |   static const int External = 4;
 | 
| 973 | #endif
 | 
| 974 |   enum no_name {
 | 
| 975 |     Record = 1,
 | 
| 976 |     Array = 2,
 | 
| 977 |     Leaf = 3,
 | 
| 978 |     External = 4,
 | 
| 979 |   };
 | 
| 980 | };
 | 
| 981 | 
 | 
| 982 | struct scope_e {
 | 
| 983 |   enum no_name {
 | 
| 984 |     Record = 1,
 | 
| 985 |     Array = 2,
 | 
| 986 |     Leaf = 3,
 | 
| 987 |     External = 4,
 | 
| 988 |   };
 | 
| 989 | };
 | 
| 990 | 
 | 
| 991 | enum Other {
 | 
| 992 |   Record = 2,
 | 
| 993 | };
 | 
| 994 | 
 | 
| 995 | };  // namespace hnode_asdl
 | 
| 996 | 
 | 
| 997 | using hnode_asdl::hnode_e;
 | 
| 998 | using runtime_asdl::lvalue;
 | 
| 999 | // namespace lvalue = runtime_asdl::lvalue;
 | 
| 1000 | 
 | 
| 1001 | TEST asdl_namespace_demo() {
 | 
| 1002 |   lvalue::Named n;
 | 
| 1003 |   lvalue::Indexed i;
 | 
| 1004 | 
 | 
| 1005 |   (void)n;
 | 
| 1006 |   (void)i;
 | 
| 1007 | 
 | 
| 1008 |   log("Record = %d", hnode_e::Record);
 | 
| 1009 |   log("Array = %d", hnode_e::Array);
 | 
| 1010 | 
 | 
| 1011 |   // In Python, it's lvalue.Named(), not lvalue__Named
 | 
| 1012 |   //
 | 
| 1013 |   // Although you could change that everywhere
 | 
| 1014 |   //
 | 
| 1015 |   // from _devbuild.gen.runtime_asdl import lvalue
 | 
| 1016 |   //
 | 
| 1017 |   // can you reverse it?
 | 
| 1018 | 
 | 
| 1019 |   PASS();
 | 
| 1020 | }
 | 
| 1021 | 
 | 
| 1022 | GREATEST_MAIN_DEFS();
 | 
| 1023 | 
 | 
| 1024 | int main(int argc, char** argv) {
 | 
| 1025 |   gHeap.Init(1 << 20);
 | 
| 1026 | 
 | 
| 1027 |   GREATEST_MAIN_BEGIN();
 | 
| 1028 | 
 | 
| 1029 |   RUN_TEST(typed_arith_parse::namespace_demo);
 | 
| 1030 | 
 | 
| 1031 |   RUN_TEST(test_misc);
 | 
| 1032 |   RUN_TEST(map_demo);
 | 
| 1033 |   RUN_TEST(shared_ptr_demo);
 | 
| 1034 |   RUN_TEST(template_demo);
 | 
| 1035 |   RUN_TEST(default_args_demo);
 | 
| 1036 |   RUN_TEST(sizeof_demo);
 | 
| 1037 |   RUN_TEST(except_demo);
 | 
| 1038 |   RUN_TEST(static_literals);
 | 
| 1039 |   RUN_TEST(enum_demo);
 | 
| 1040 |   RUN_TEST(field_mask_demo);
 | 
| 1041 |   // RUN_TEST(smartptr_inheritance_demo);
 | 
| 1042 | 
 | 
| 1043 |   RUN_TEST(mmap_demo);
 | 
| 1044 |   RUN_TEST(comma_demo);
 | 
| 1045 |   RUN_TEST(signed_unsigned_demo);
 | 
| 1046 |   RUN_TEST(param_passing_demo);
 | 
| 1047 | 
 | 
| 1048 |   RUN_TEST(tea_macros_demo);
 | 
| 1049 |   RUN_TEST(tea_interface);
 | 
| 1050 | 
 | 
| 1051 |   RUN_TEST(asdl_namespace_demo);
 | 
| 1052 | 
 | 
| 1053 |   GREATEST_MAIN_END(); /* display results */
 | 
| 1054 |   return 0;
 | 
| 1055 | }
 |