OILS / mycpp / demo / target_lang.cc View on Github | oilshell.org

1055 lines, 599 significant
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
27using std::unordered_map;
28
29class 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
48class 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
58template <class T>
59class 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
83class FatalError {};
84
85class 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
100int 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
107int parse(const char* text) {
108 if (text[0] == 'f') {
109 throw ParseError("started with f");
110 }
111 return 0;
112}
113
114void throw_fatal() {
115 throw FatalError();
116}
117
118void 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
129TEST 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
170TEST 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
186void f(int a, int b = -1, const char* s = nullptr);
187
188void 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
195class 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
213Foo::Foo(int i, bool always_strict) : i(i), always_strict(always_strict) {
214}
215
216TEST 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
229namespace core {
230namespace util {
231void p_die(const char* s) {
232 log("p_die %s", s);
233}
234} // namespace util
235} // namespace core
236
237namespace tdop {
238using core::util::p_die;
239
240class 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
250namespace typed_arith_parse {
251// using namespace core; This makes EVERYTHING available.
252
253namespace util = core::util;
254
255// This lets us use "Parser""
256using tdop::Parser;
257
258TEST 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.
280struct 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
288class expr__Const {
289 public:
290 expr__Const(int i) : i_(i) {
291 }
292 int i_;
293};
294
295namespace expr {
296typedef expr__Const Const;
297}
298
299using std::make_shared;
300using std::shared_ptr;
301
302shared_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
307TEST 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
333TEST 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
362TEST 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
375TEST 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
392struct Point {
393 int x;
394 int y;
395};
396
397// structs don't have any constructors, so don't need any constexpr stuff
398constexpr Point p = {3, 4};
399
400// members must be public to allow initializer list
401class 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
420constexpr PointC pc = {5, 6};
421
422class 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
431constexpr SubPointC sub = {7, 8};
432
433class Compound {
434 public:
435 PointC c1;
436 PointC c2;
437};
438
439// This works, but what about pointers?
440constexpr Compound c = {{0, 1}, {8, 9}};
441
442TEST 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
464enum class Color_e { red, blue };
465
466TEST 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
482class 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
493constexpr 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
517constexpr uint16_t Node_mask() {
518 return (1 << (offsetof(Node, left) / sizeof(void*)) |
519 1 << (offsetof(Node, right) / sizeof(void*)));
520}
521
522#endif
523
524void 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
534TEST 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
545class 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
557class 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
569TEST 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
585char* 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
597TEST 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
620TEST 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
640template <typename T>
641[[gnu::warning("your type here")]] bool print_type() {
642 return true;
643}
644
645TEST 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
665class Object {
666 public:
667 uint32_t header;
668};
669
670class 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
680void 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
686Writer* gStdout = nullptr;
687
688Writer* 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
708TEST 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
735TEST 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
840class Reader1 {
841 public:
842 virtual int Read(int n) = 0;
843};
844
845// Second package
846class Reader2 {
847 public:
848 virtual int Read(int n) = 0;
849};
850
851class 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!
860class 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
873class Concrete1 : public Reader1 {};
874
875class 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
894TEST 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
924namespace runtime_asdl {
925
926class lvalue_t {};
927
928class lvalue__Named : public lvalue_t {};
929
930class lvalue__Indexed : public lvalue_t {};
931
932#if 0
933namespace 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"
940struct 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
956namespace hnode_asdl {
957#if 0
958namespace 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
967struct 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
982struct scope_e {
983 enum no_name {
984 Record = 1,
985 Array = 2,
986 Leaf = 3,
987 External = 4,
988 };
989};
990
991enum Other {
992 Record = 2,
993};
994
995}; // namespace hnode_asdl
996
997using hnode_asdl::hnode_e;
998using runtime_asdl::lvalue;
999// namespace lvalue = runtime_asdl::lvalue;
1000
1001TEST 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
1022GREATEST_MAIN_DEFS();
1023
1024int 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}