cpp

Coverage Report

Created: 2022-07-20 01:16

/home/uke/oil/mycpp/mylib_leaky.h
Line
Count
Source (jump to first uncovered line)
1
// mylib_leaky.h
2
3
#ifndef MYLIB_H
4
#define MYLIB_H
5
6
#include <assert.h>
7
#include <ctype.h>   // isalpha(), isdigit()
8
#include <stdlib.h>  // malloc
9
#include <string.h>  // strlen
10
11
#include <algorithm>  // sort() is templated
12
// https://stackoverflow.com/questions/3882346/forward-declare-file
13
#include <climits>  // CHAR_BIT
14
#include <cstdint>
15
#include <cstdio>  // FILE*
16
#include <initializer_list>
17
#include <vector>
18
19
#include "common.h"
20
21
// if this file is even included, we're using the old mylib
22
#define MYLIB_LEAKY 1
23
#include "gc_heap.h"  // for Obj
24
25
#ifdef DUMB_ALLOC
26
#include "cpp/leaky_dumb_alloc.h"
27
186
#define malloc dumb_malloc
28
6
#define free dumb_free
29
#endif
30
31
class Str;
32
33
template <class T>
34
class List;
35
36
template <class K, class V>
37
class Dict;
38
39
template <class K, class V>
40
class DictIter;
41
42
bool are_equal(Str* left, Str* right);
43
bool str_equals(Str* left, Str* right);
44
45
namespace mylib {
46
template <typename V>
47
void dict_remove(Dict<Str*, V>* haystack, Str* needle);
48
49
template <typename V>
50
void dict_remove(Dict<int, V>* haystack, int needle);
51
52
};  // namespace mylib
53
54
extern Str* kEmptyString;
55
56
void print(Str* s);
57
58
// log() generates code that writes this
59
void println_stderr(Str* s);
60
61
class IndexError {};
62
class ValueError {};
63
class KeyError {};
64
65
class EOFError {};
66
67
class NotImplementedError {
68
 public:
69
0
  NotImplementedError() {
70
0
  }
71
0
  explicit NotImplementedError(int i) {  // e.g. in expr_to_ast
72
0
  }
73
0
  explicit NotImplementedError(const char* s) {
74
0
  }
75
0
  explicit NotImplementedError(Str* s) {
76
0
  }
77
};
78
79
class AssertionError {
80
 public:
81
0
  AssertionError() {
82
0
  }
83
0
  explicit AssertionError(int i) {  // e.g. in expr_to_ast
84
0
  }
85
0
  explicit AssertionError(const char* s) {
86
0
  }
87
0
  explicit AssertionError(Str* s) {
88
0
  }
89
};
90
91
// Python's RuntimeError looks like this.  . libc::regex_match and other
92
// bindings raise it.
93
class RuntimeError {
94
 public:
95
0
  RuntimeError(Str* message) : message(message) {
96
0
  }
97
  Str* message;
98
};
99
100
//
101
// Data Types
102
//
103
104
#ifdef MYLIB_LEAKY
105
class Str : public gc_heap::Obj {
106
 public:
107
  Str(const char* data, int len)
108
      : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0),
109
        len_(len),
110
614
        data_(data) {
111
614
  }
112
113
238
  explicit Str(const char* data) : Str(data, strlen(data)) {
114
238
  }
115
116
  // emulating gc_heap API
117
1
  void SetObjLenFromStrLen(int len) {
118
1
    len_ = len;
119
1
  }
120
121
  // Usage:
122
  // Str* s = OverAllocatedStr(10);
123
  // strcpy(s->data(), "foo");
124
36
  char* data() {
125
36
    return const_cast<char*>(data_);
126
36
  }
127
128
  // Important invariant: the buffer is of size len+1, so data[len] is OK to
129
  // access!  Not just data[len-1].  We use that to test if it's a C string.
130
  // note: "foo" and "foo\0" are both NUL-terminated.
131
49
  bool IsNulTerminated() {
132
49
    return data_[len_] == '\0';
133
49
  }
134
135
  // Get a string with one character
136
0
  Str* index_(int i) {
137
0
    if (i < 0) {
138
0
      i = len_ + i;
139
0
    }
140
0
    assert(i >= 0);
141
0
    assert(i < len_);  // had a problem here!
142
0
143
0
    char* buf = static_cast<char*>(malloc(2));
144
0
    buf[0] = data_[i];
145
0
    buf[1] = '\0';
146
0
    return new Str(buf, 1);
147
0
  }
148
149
  // s[begin:]
150
0
  Str* slice(int begin) {
151
0
    if (begin == 0) {
152
0
      return this;  // s[i:] where i == 0 is common in here docs
153
0
    }
154
0
    if (begin < 0) {
155
0
      begin = len_ + begin;
156
0
    }
157
0
    return slice(begin, len_);
158
0
  }
159
  // s[begin:end]
160
307
  Str* slice(int begin, int end) {
161
307
    begin = std::min(begin, len_);
162
307
    end = std::min(end, len_);
163
164
307
    assert(begin <= len_);
165
0
    assert(end <= len_);
166
167
307
    if (begin < 0) {
168
140
      begin = len_ + begin;
169
140
    }
170
171
307
    if (end < 0) {
172
140
      end = len_ + end;
173
140
    }
174
175
307
    begin = std::min(begin, len_);
176
307
    end = std::min(end, len_);
177
178
307
    begin = std::max(begin, 0);
179
307
    end = std::max(end, 0);
180
181
307
    assert(begin >= 0);
182
0
    assert(begin <= len_);
183
184
0
    assert(end >= 0);
185
0
    assert(end <= len_);
186
187
0
    int new_len = end - begin;
188
189
    // Tried to use std::clamp() here but we're not compiling against cxx-17
190
307
    new_len = std::max(new_len, 0);
191
307
    new_len = std::min(new_len, len_);
192
193
    /* printf("len(%d) [%d, %d] newlen(%d)\n",  len_, begin, end, new_len); */
194
195
307
    assert(new_len >= 0);
196
0
    assert(new_len <= len_);
197
198
0
    char* buf = static_cast<char*>(malloc(new_len + 1));
199
307
    memcpy(buf, data_ + begin, new_len);
200
201
307
    buf[new_len] = '\0';
202
307
    return new Str(buf, new_len);
203
307
  }
204
205
  // Helper for lstrip() and strip()
206
5
  int _strip_left_pos() {
207
5
    assert(len_ > 0);
208
209
0
    int i = 0;
210
5
    bool done = false;
211
10
    while (i < len_ && !done) {
212
5
      switch (data_[i]) {
213
0
      case ' ':
214
0
      case '\t':
215
0
      case '\r':
216
0
      case '\n':
217
0
        i++;
218
5
      default:
219
5
        done = true;
220
5
        break;
221
5
      }
222
5
    }
223
5
    return i;
224
5
  }
225
226
  // Helper for rstrip() and strip()
227
5
  int _strip_right_pos() {
228
5
    assert(len_ > 0);
229
230
0
    int last = len_ - 1;
231
5
    int i = last;
232
5
    bool done = false;
233
10
    while (i > 0 && !done) {
234
5
      switch (data_[i]) {
235
5
      case ' ':
236
5
      case '\t':
237
5
      case '\r':
238
5
      case '\n':
239
5
        i--;
240
5
      default:
241
5
        done = true;
242
5
        break;
243
5
      }
244
5
    }
245
5
    return i;
246
5
  }
247
248
5
  Str* strip() {
249
5
    if (len_ == 0) {
250
0
      return this;
251
0
    }
252
5
    int left_pos = _strip_left_pos();
253
5
    int right_pos = _strip_right_pos();
254
255
5
    if (left_pos == 0 && right_pos == len_ - 1) {
256
0
      return this;
257
0
    }
258
259
    // cstring-NOTE: This returns a SLICE, not a copy, unlike rstrip()
260
    // TODO: make them consistent.
261
5
    int len = right_pos - left_pos + 1;
262
5
    return new Str(data_ + left_pos, len);
263
5
  }
264
265
  // Used for CommandSub in osh/cmd_exec.py
266
0
  Str* rstrip(Str* chars) {
267
0
    assert(chars->len_ == 1);
268
0
    char c = chars->data_[0];
269
0
270
0
    int last = len_ - 1;
271
0
    int i = last;
272
0
    bool done = false;
273
0
    while (i > 0 && !done) {
274
0
      if (data_[i] == c) {
275
0
        i--;
276
0
      } else {
277
0
        done = true;
278
0
        break;
279
0
      }
280
0
    }
281
0
    if (i == last) {  // nothing stripped
282
0
      return this;
283
0
    }
284
0
    int new_len = i + 1;
285
0
    char* buf = static_cast<char*>(malloc(new_len + 1));
286
0
    memcpy(buf, data_, new_len);
287
0
    buf[new_len] = '\0';
288
0
    return new Str(buf, new_len);
289
0
  }
290
291
0
  Str* rstrip() {
292
0
    if (len_ == 0) {
293
0
      return this;
294
0
    }
295
0
    int right_pos = _strip_right_pos();
296
0
    if (right_pos == len_ - 1) {  // nothing stripped
297
0
      return this;
298
0
    }
299
0
    int new_len = right_pos + 1;
300
0
    char* buf = static_cast<char*>(malloc(new_len + 1));
301
0
    memcpy(buf, data_, new_len);
302
0
    buf[new_len] = '\0';
303
0
    return new Str(buf, new_len);
304
0
  }
305
306
0
  bool startswith(Str* s) {
307
0
    if (s->len_ > len_) {
308
0
      return false;
309
0
    }
310
0
    return memcmp(data_, s->data_, s->len_) == 0;
311
0
  }
312
0
  bool endswith(Str* s) {
313
0
    if (s->len_ > len_) {
314
0
      return false;
315
0
    }
316
0
    const char* start = data_ + len_ - s->len_;
317
0
    return memcmp(start, s->data_, s->len_) == 0;
318
0
  }
319
0
  bool isdigit() {
320
0
    if (len_ == 0) {
321
0
      return false;  // special case
322
0
    }
323
0
    for (int i = 0; i < len_; ++i) {
324
0
      if (!::isdigit(data_[i])) {
325
0
        return false;
326
0
      }
327
0
    }
328
0
    return true;
329
0
  }
330
0
  bool isalpha() {
331
0
    if (len_ == 0) {
332
0
      return false;  // special case
333
0
    }
334
0
    for (int i = 0; i < len_; ++i) {
335
0
      if (!::isalpha(data_[i])) {
336
0
        return false;
337
0
      }
338
0
    }
339
0
    return true;
340
0
  }
341
  // e.g. for osh/braces.py
342
0
  bool isupper() {
343
0
    if (len_ == 0) {
344
0
      return false;  // special case
345
0
    }
346
0
    for (int i = 0; i < len_; ++i) {
347
0
      if (!::isupper(data_[i])) {
348
0
        return false;
349
0
      }
350
0
    }
351
0
    return true;
352
0
  }
353
354
  List<Str*>* split(Str* sep);
355
  List<Str*>* splitlines(bool keep);
356
  Str* join(List<Str*>* items);
357
358
  Str* replace(Str* old, Str* new_str);
359
360
0
  int find(Str* needle, int pos = 0) {
361
0
    assert(needle->len_ == 1);  // Oil's usage
362
0
    char c = needle->data_[0];
363
0
    for (int i = pos; i < len_; ++i) {
364
0
      if (data_[i] == c) {
365
0
        return i;
366
0
      }
367
0
    }
368
0
    return -1;
369
0
  }
370
371
0
  int rfind(Str* needle) {
372
0
    assert(needle->len_ == 1);  // Oil's usage
373
0
    char c = needle->data_[0];
374
0
    for (int i = len_ - 1; i >= 0; --i) {
375
0
      if (data_[i] == c) {
376
0
        return i;
377
0
      }
378
0
    }
379
0
    return -1;
380
0
  }
381
382
  Str* upper();
383
  Str* lower();
384
  Str* ljust(int width, Str* fillchar);
385
  Str* rjust(int width, Str* fillchar);
386
387
  int len_;  // reorder for alignment
388
  const char* data_;
389
390
  DISALLOW_COPY_AND_ASSIGN(Str)
391
};
392
393
// NOTE: This iterates over bytes.
394
class StrIter {
395
 public:
396
0
  explicit StrIter(Str* s) : s_(s), i_(0) {
397
0
  }
398
  void Next() {
399
    i_++;
400
  }
401
  bool Done() {
402
    return i_ >= s_->len_;
403
  }
404
  Str* Value();
405
406
 private:
407
  Str* s_;
408
  int i_;
409
410
  DISALLOW_COPY_AND_ASSIGN(StrIter)
411
};
412
413
// TODO: Rewrite without vector<>, so we don't depend on libstdc++.
414
//
415
// TODO(Jesse): I like the sound of getting rid of std:vector
416
//
417
template <class T>
418
class List : public gc_heap::Obj {
419
 public:
420
  // Note: constexpr doesn't work because the std::vector destructor is
421
  // nontrivial
422
17
  List() : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0), v_() {
423
    // Note: this seems to INCREASE the number of 'new' calls.  I guess because
424
    // many 'spids' lists aren't used?
425
    // v_.reserve(64);
426
17
  }
_ZN4ListIP3StrEC2Ev
Line
Count
Source
422
16
  List() : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0), v_() {
423
    // Note: this seems to INCREASE the number of 'new' calls.  I guess because
424
    // many 'spids' lists aren't used?
425
    // v_.reserve(64);
426
16
  }
_ZN4ListIiEC2Ev
Line
Count
Source
422
1
  List() : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0), v_() {
423
    // Note: this seems to INCREASE the number of 'new' calls.  I guess because
424
    // many 'spids' lists aren't used?
425
    // v_.reserve(64);
426
1
  }
Unexecuted instantiation: _ZN4ListIP6Tuple2IiP3StrEEC2Ev
427
428
  // Used by list_repeat
429
  List(T item, int n)
430
2
      : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0), v_(n, item) {
431
2
  }
_ZN4ListIP3StrEC2ES1_i
Line
Count
Source
430
1
      : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0), v_(n, item) {
431
1
  }
_ZN4ListIbEC2Ebi
Line
Count
Source
430
1
      : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0), v_(n, item) {
431
1
  }
432
433
  List(std::initializer_list<T> init)
434
8
      : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0), v_() {
435
23
    for (T item : init) {
436
23
      v_.push_back(item);
437
23
    }
438
8
  }
_ZN4ListIiEC2ESt16initializer_listIiE
Line
Count
Source
434
7
      : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0), v_() {
435
20
    for (T item : init) {
436
20
      v_.push_back(item);
437
20
    }
438
7
  }
_ZN4ListIdEC2ESt16initializer_listIdE
Line
Count
Source
434
1
      : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0), v_() {
435
3
    for (T item : init) {
436
3
      v_.push_back(item);
437
3
    }
438
1
  }
Unexecuted instantiation: _ZN4ListIP3StrEC2ESt16initializer_listIS1_E
439
440
  // a[-1] = 42 becomes a->set(-1, 42);
441
2
  void set(int index, T value) {
442
2
    if (index < 0) {
443
0
      index = v_.size() + index;
444
0
    }
445
2
    v_[index] = value;
446
2
  }
447
448
  // L[i]
449
51
  T index_(int i) const {
450
51
    if (i < 0) {
451
      // User code doesn't result in mylist[-1], but Oil's own code does
452
0
      int j = v_.size() + i;
453
0
      return v_.at(j);
454
0
    }
455
51
    return v_.at(i);  // checked version
456
51
  }
_ZNK4ListIiE6index_Ei
Line
Count
Source
449
25
  T index_(int i) const {
450
25
    if (i < 0) {
451
      // User code doesn't result in mylist[-1], but Oil's own code does
452
0
      int j = v_.size() + i;
453
0
      return v_.at(j);
454
0
    }
455
25
    return v_.at(i);  // checked version
456
25
  }
_ZNK4ListIbE6index_Ei
Line
Count
Source
449
2
  T index_(int i) const {
450
2
    if (i < 0) {
451
      // User code doesn't result in mylist[-1], but Oil's own code does
452
0
      int j = v_.size() + i;
453
0
      return v_.at(j);
454
0
    }
455
2
    return v_.at(i);  // checked version
456
2
  }
_ZNK4ListIdE6index_Ei
Line
Count
Source
449
4
  T index_(int i) const {
450
4
    if (i < 0) {
451
      // User code doesn't result in mylist[-1], but Oil's own code does
452
0
      int j = v_.size() + i;
453
0
      return v_.at(j);
454
0
    }
455
4
    return v_.at(i);  // checked version
456
4
  }
_ZNK4ListIP3StrE6index_Ei
Line
Count
Source
449
20
  T index_(int i) const {
450
20
    if (i < 0) {
451
      // User code doesn't result in mylist[-1], but Oil's own code does
452
0
      int j = v_.size() + i;
453
0
      return v_.at(j);
454
0
    }
455
20
    return v_.at(i);  // checked version
456
20
  }
457
458
  // L.index(i) -- Python method
459
  int index(T value) const {
460
    int len = v_.size();
461
    for (int i = 0; i < len; i++) {
462
      // TODO: this doesn't work for strings!
463
      if (v_[i] == value) {
464
        return i;
465
      }
466
    }
467
    throw new ValueError();
468
  }
469
470
  // L[begin:]
471
  List* slice(int begin) {
472
    if (begin == 0) {
473
      return this;
474
    }
475
    if (begin < 0) {
476
      begin = v_.size() + begin;
477
    }
478
479
    List* result = new List();
480
    int len = v_.size();
481
    for (int i = begin; i < len; i++) {
482
      result->v_.push_back(v_[i]);
483
    }
484
    return result;
485
  }
486
  // L[begin:end]
487
  // TODO: Can this be optimized?
488
  List* slice(int begin, int end) {
489
    if (begin < 0) {
490
      begin = v_.size() + begin;
491
    }
492
    if (end < 0) {
493
      end = v_.size() + end;
494
    }
495
496
    List* result = new List();
497
    for (int i = begin; i < end; i++) {
498
      result->v_.push_back(v_[i]);
499
    }
500
    return result;
501
  }
502
503
38
  void append(T item) {
504
#ifdef ALLOC_LOG
505
    // we can post process this format to find large lists
506
    // except when they're constants, but that's OK?
507
    printf("%p %zu\n", this, v_.size());
508
#endif
509
510
38
    v_.push_back(item);
511
38
  }
_ZN4ListIiE6appendEi
Line
Count
Source
503
4
  void append(T item) {
504
#ifdef ALLOC_LOG
505
    // we can post process this format to find large lists
506
    // except when they're constants, but that's OK?
507
    printf("%p %zu\n", this, v_.size());
508
#endif
509
510
4
    v_.push_back(item);
511
4
  }
_ZN4ListIP3StrE6appendES1_
Line
Count
Source
503
34
  void append(T item) {
504
#ifdef ALLOC_LOG
505
    // we can post process this format to find large lists
506
    // except when they're constants, but that's OK?
507
    printf("%p %zu\n", this, v_.size());
508
#endif
509
510
34
    v_.push_back(item);
511
34
  }
Unexecuted instantiation: _ZN4ListIP6Tuple2IiP3StrEE6appendES4_
512
513
  void extend(List<T>* items) {
514
    // Note: C++ idioms would be v_.insert() or std::copy, but we're keeping it
515
    // simple.
516
    //
517
    // We could optimize this for the small cases Oil has?  I doubt it's a
518
    // bottleneck anywhere.
519
    int len = items->v_.size();
520
    for (int i = 0; i < len; ++i) {
521
      v_.push_back(items->v_[i]);
522
    }
523
  }
524
525
  // Reconsider?
526
  // https://stackoverflow.com/questions/12600330/pop-back-return-value
527
  T pop() {
528
    assert(!v_.empty());
529
    T result = v_.back();
530
    v_.pop_back();
531
    return result;
532
  }
533
534
  // Used in osh/word_parse.py to remove from front
535
  // TODO: Don't accept arbitrary index?
536
1
  T pop(int index) {
537
1
    if (v_.size() == 0) {
538
      // TODO(Jesse): Probably shouldn't crash if we try to pop a List with
539
      // nothing on it
540
0
      InvalidCodePath();
541
0
    }
542
543
0
    T result = v_.at(index);
544
1
    v_.erase(v_.begin() + index);
545
1
    return result;
546
547
    /*
548
    Implementation without std::vector
549
    assert(index == 0);
550
    for (int i = 1; i < v_.size(); ++i) {
551
      v_[i-1] = v_[i];
552
    }
553
    v_.pop_back();
554
    */
555
1
  }
556
557
  void clear() {
558
    v_.clear();
559
  }
560
561
2
  void sort() {
562
2
    mysort(&v_);
563
2
  }
564
565
  // in osh/string_ops.py
566
2
  void reverse() {
567
2
    int n = v_.size();
568
4
    for (int i = 0; i < n / 2; ++i) {
569
      // log("swapping %d and %d", i, n-i);
570
2
      T tmp = v_[i];
571
2
      int j = n - 1 - i;
572
2
      v_[i] = v_[j];
573
2
      v_[j] = tmp;
574
2
    }
575
2
  }
576
577
  // private:
578
  std::vector<T> v_;  // ''.join accesses this directly
579
};
580
581
template <class T>
582
class ListIter {
583
 public:
584
1
  explicit ListIter(List<T>* L) : L_(L), i_(0) {
585
1
  }
586
16
  void Next() {
587
16
    i_++;
588
16
  }
589
21
  bool Done() {
590
    // "unsigned size_t was a mistake"
591
21
    return i_ >= static_cast<int>(L_->v_.size());
592
21
  }
593
16
  T Value() {
594
16
    return L_->v_[i_];
595
16
  }
596
597
 private:
598
  List<T>* L_;
599
  int i_;
600
};
601
602
// TODO: Does using pointers rather than indices make this more efficient?
603
template <class T>
604
class ReverseListIter {
605
 public:
606
1
  explicit ReverseListIter(List<T>* L) : L_(L), i_(L_->v_.size() - 1) {
607
1
  }
608
3
  void Next() {
609
3
    i_--;
610
3
  }
611
4
  bool Done() {
612
4
    return i_ < 0;
613
4
  }
614
3
  T Value() {
615
3
    return L_->v_[i_];
616
3
  }
617
618
 private:
619
  List<T>* L_;
620
  int i_;
621
};
622
623
// TODO: A proper dict index should get rid of this unusual sentinel scheme.
624
// The index can be -1 on deletion, regardless of the type of the key.
625
626
template <class K, class V>
627
void dict_next(DictIter<K, V>* it, const std::vector<std::pair<K, V>>& items) {
628
  ++it->i_;
629
}
630
631
template <class V>
632
void dict_next(DictIter<Str*, V>* it,
633
2
               const std::vector<std::pair<Str*, V>>& items) {
634
2
  while (true) {
635
2
    ++it->i_;
636
2
    if (it->Done()) {
637
1
      break;
638
1
    }
639
1
    if (items[it->i_].first) {  // not nullptr
640
1
      break;
641
1
    }
642
1
  }
643
2
}
_Z9dict_nextIiEvP8DictIterIP3StrT_ERKSt6vectorISt4pairIS2_S3_ESaIS8_EE
Line
Count
Source
633
2
               const std::vector<std::pair<Str*, V>>& items) {
634
2
  while (true) {
635
2
    ++it->i_;
636
2
    if (it->Done()) {
637
1
      break;
638
1
    }
639
1
    if (items[it->i_].first) {  // not nullptr
640
1
      break;
641
1
    }
642
1
  }
643
2
}
Unexecuted instantiation: _Z9dict_nextIP3StrEvP8DictIterIS1_T_ERKSt6vectorISt4pairIS1_S3_ESaIS8_EE
644
645
template <class K, class V>
646
bool dict_done(DictIter<K, V>* it, const std::vector<std::pair<K, V>>& items) {
647
  int n = items.size();
648
  return it->i_ >= n;
649
}
650
651
template <class V>
652
bool dict_done(DictIter<Str*, V>* it,
653
7
               const std::vector<std::pair<Str*, V>>& items) {
654
7
  int n = items.size();
655
7
  if (it->i_ >= n) {
656
1
    return true;
657
1
  }
658
9
  for (int j = it->i_; j < n; ++j) {
659
6
    if (items[j].first) {  // there's still something left
660
3
      return false;
661
3
    }
662
6
  }
663
3
  return true;
664
6
}
_Z9dict_doneIiEbP8DictIterIP3StrT_ERKSt6vectorISt4pairIS2_S3_ESaIS8_EE
Line
Count
Source
653
6
               const std::vector<std::pair<Str*, V>>& items) {
654
6
  int n = items.size();
655
6
  if (it->i_ >= n) {
656
1
    return true;
657
1
  }
658
7
  for (int j = it->i_; j < n; ++j) {
659
5
    if (items[j].first) {  // there's still something left
660
3
      return false;
661
3
    }
662
5
  }
663
2
  return true;
664
5
}
_Z9dict_doneIP3StrEbP8DictIterIS1_T_ERKSt6vectorISt4pairIS1_S3_ESaIS8_EE
Line
Count
Source
653
1
               const std::vector<std::pair<Str*, V>>& items) {
654
1
  int n = items.size();
655
1
  if (it->i_ >= n) {
656
0
    return true;
657
0
  }
658
2
  for (int j = it->i_; j < n; ++j) {
659
1
    if (items[j].first) {  // there's still something left
660
0
      return false;
661
0
    }
662
1
  }
663
1
  return true;
664
1
}
665
666
template <class K, class V>
667
class DictIter {
668
 public:
669
3
  explicit DictIter(Dict<K, V>* D) : D_(D), i_(0) {
670
3
  }
_ZN8DictIterIP3StriEC2EP4DictIS1_iE
Line
Count
Source
669
2
  explicit DictIter(Dict<K, V>* D) : D_(D), i_(0) {
670
2
  }
_ZN8DictIterIP3StrS1_EC2EP4DictIS1_S1_E
Line
Count
Source
669
1
  explicit DictIter(Dict<K, V>* D) : D_(D), i_(0) {
670
1
  }
671
2
  void Next() {
672
2
    dict_next(this, D_->items_);
673
2
  }
_ZN8DictIterIP3StriE4NextEv
Line
Count
Source
671
2
  void Next() {
672
2
    dict_next(this, D_->items_);
673
2
  }
Unexecuted instantiation: _ZN8DictIterIP3StrS1_E4NextEv
674
7
  bool Done() {
675
7
    return dict_done(this, D_->items_);
676
7
  }
_ZN8DictIterIP3StriE4DoneEv
Line
Count
Source
674
6
  bool Done() {
675
6
    return dict_done(this, D_->items_);
676
6
  }
_ZN8DictIterIP3StrS1_E4DoneEv
Line
Count
Source
674
1
  bool Done() {
675
1
    return dict_done(this, D_->items_);
676
1
  }
677
2
  K Key() {
678
2
    return D_->items_[i_].first;
679
2
  }
_ZN8DictIterIP3StriE3KeyEv
Line
Count
Source
677
2
  K Key() {
678
2
    return D_->items_[i_].first;
679
2
  }
Unexecuted instantiation: _ZN8DictIterIP3StrS1_E3KeyEv
680
0
  V Value() {
681
0
    return D_->items_[i_].second;
682
0
  }
683
684
  Dict<K, V>* D_;
685
  int i_;
686
};
687
688
// Specialized functions
689
template <class V>
690
29
int find_by_key(const std::vector<std::pair<Str*, V>>& items, Str* key) {
691
29
  int n = items.size();
692
87
  for (int i = 0; i < n; ++i) {
693
66
    Str* s = items[i].first;  // nullptr for deleted entries
694
66
    if (s && str_equals(s, key)) {
695
8
      return i;
696
8
    }
697
66
  }
698
21
  return -1;
699
29
}
_Z11find_by_keyIiEiRKSt6vectorISt4pairIP3StrT_ESaIS5_EES3_
Line
Count
Source
690
15
int find_by_key(const std::vector<std::pair<Str*, V>>& items, Str* key) {
691
15
  int n = items.size();
692
34
  for (int i = 0; i < n; ++i) {
693
26
    Str* s = items[i].first;  // nullptr for deleted entries
694
26
    if (s && str_equals(s, key)) {
695
7
      return i;
696
7
    }
697
26
  }
698
8
  return -1;
699
15
}
_Z11find_by_keyIP3StrEiRKSt6vectorISt4pairIS1_T_ESaIS5_EES1_
Line
Count
Source
690
2
int find_by_key(const std::vector<std::pair<Str*, V>>& items, Str* key) {
691
2
  int n = items.size();
692
2
  for (int i = 0; i < n; ++i) {
693
1
    Str* s = items[i].first;  // nullptr for deleted entries
694
1
    if (s && str_equals(s, key)) {
695
1
      return i;
696
1
    }
697
1
  }
698
1
  return -1;
699
2
}
_Z11find_by_keyIPN12runtime_asdl7value_tEEiRKSt6vectorISt4pairIP3StrT_ESaIS8_EES6_
Line
Count
Source
690
12
int find_by_key(const std::vector<std::pair<Str*, V>>& items, Str* key) {
691
12
  int n = items.size();
692
51
  for (int i = 0; i < n; ++i) {
693
39
    Str* s = items[i].first;  // nullptr for deleted entries
694
39
    if (s && str_equals(s, key)) {
695
0
      return i;
696
0
    }
697
39
  }
698
12
  return -1;
699
12
}
700
701
template <class V>
702
6
int find_by_key(const std::vector<std::pair<int, V>>& items, int key) {
703
6
  int n = items.size();
704
8
  for (int i = 0; i < n; ++i) {
705
5
    if (items[i].first == key) {
706
3
      return i;
707
3
    }
708
5
  }
709
3
  return -1;
710
6
}
711
712
template <class V>
713
4
List<Str*>* dict_keys(const std::vector<std::pair<Str*, V>>& items) {
714
4
  auto result = new List<Str*>();
715
4
  int n = items.size();
716
14
  for (int i = 0; i < n; ++i) {
717
10
    Str* s = items[i].first;  // nullptr for deleted entries
718
10
    if (s) {
719
10
      result->append(s);
720
10
    }
721
10
  }
722
4
  return result;
723
4
}
_Z9dict_keysIiEP4ListIP3StrERKSt6vectorISt4pairIS2_T_ESaIS8_EE
Line
Count
Source
713
3
List<Str*>* dict_keys(const std::vector<std::pair<Str*, V>>& items) {
714
3
  auto result = new List<Str*>();
715
3
  int n = items.size();
716
12
  for (int i = 0; i < n; ++i) {
717
9
    Str* s = items[i].first;  // nullptr for deleted entries
718
9
    if (s) {
719
9
      result->append(s);
720
9
    }
721
9
  }
722
3
  return result;
723
3
}
_Z9dict_keysIP3StrEP4ListIS1_ERKSt6vectorISt4pairIS1_T_ESaIS8_EE
Line
Count
Source
713
1
List<Str*>* dict_keys(const std::vector<std::pair<Str*, V>>& items) {
714
1
  auto result = new List<Str*>();
715
1
  int n = items.size();
716
2
  for (int i = 0; i < n; ++i) {
717
1
    Str* s = items[i].first;  // nullptr for deleted entries
718
1
    if (s) {
719
1
      result->append(s);
720
1
    }
721
1
  }
722
1
  return result;
723
1
}
724
725
template <class V>
726
2
List<V>* dict_values(const std::vector<std::pair<Str*, V>>& items) {
727
2
  auto result = new List<V>();
728
2
  int n = items.size();
729
6
  for (int i = 0; i < n; ++i) {
730
4
    auto& pair = items[i];
731
4
    if (pair.first) {
732
4
      result->append(pair.second);
733
4
    }
734
4
  }
735
2
  return result;
736
2
}
_Z11dict_valuesIiEP4ListIT_ERKSt6vectorISt4pairIP3StrS1_ESaIS8_EE
Line
Count
Source
726
1
List<V>* dict_values(const std::vector<std::pair<Str*, V>>& items) {
727
1
  auto result = new List<V>();
728
1
  int n = items.size();
729
4
  for (int i = 0; i < n; ++i) {
730
3
    auto& pair = items[i];
731
3
    if (pair.first) {
732
3
      result->append(pair.second);
733
3
    }
734
3
  }
735
1
  return result;
736
1
}
_Z11dict_valuesIP3StrEP4ListIT_ERKSt6vectorISt4pairIS1_S3_ESaIS8_EE
Line
Count
Source
726
1
List<V>* dict_values(const std::vector<std::pair<Str*, V>>& items) {
727
1
  auto result = new List<V>();
728
1
  int n = items.size();
729
2
  for (int i = 0; i < n; ++i) {
730
1
    auto& pair = items[i];
731
1
    if (pair.first) {
732
1
      result->append(pair.second);
733
1
    }
734
1
  }
735
1
  return result;
736
1
}
737
738
// Dict currently implemented by VECTOR OF PAIRS.  TODO: Use a real hash table,
739
// and measure performance.  The hash table has to beat this for small cases!
740
template <class K, class V>
741
class Dict : public gc_heap::Obj {
742
 public:
743
10
  Dict() : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0), items_() {
744
10
  }
_ZN4DictIiP3StrEC2Ev
Line
Count
Source
743
1
  Dict() : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0), items_() {
744
1
  }
_ZN4DictIP3StriEC2Ev
Line
Count
Source
743
2
  Dict() : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0), items_() {
744
2
  }
_ZN4DictIP3StrS1_EC2Ev
Line
Count
Source
743
1
  Dict() : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0), items_() {
744
1
  }
_ZN4DictIP3StrPN4args7_ActionEEC2Ev
Line
Count
Source
743
4
  Dict() : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0), items_() {
744
4
  }
_ZN4DictIP3StrPN12runtime_asdl7value_tEEC2Ev
Line
Count
Source
743
2
  Dict() : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0), items_() {
744
2
  }
745
746
  // Dummy
747
  Dict(std::initializer_list<K> keys, std::initializer_list<V> values)
748
      : gc_heap::Obj(Tag::FixedSize, gc_heap::kZeroMask, 0), items_() {
749
  }
750
751
  // d[key] in Python: raises KeyError if not found
752
5
  V index_(K key) {
753
5
    int pos = find(key);
754
5
    if (pos == -1) {
755
0
      throw new KeyError();
756
5
    } else {
757
5
      return items_[pos].second;
758
5
    }
759
5
  }
_ZN4DictIiP3StrE6index_Ei
Line
Count
Source
752
1
  V index_(K key) {
753
1
    int pos = find(key);
754
1
    if (pos == -1) {
755
0
      throw new KeyError();
756
1
    } else {
757
1
      return items_[pos].second;
758
1
    }
759
1
  }
_ZN4DictIP3StriE6index_ES1_
Line
Count
Source
752
4
  V index_(K key) {
753
4
    int pos = find(key);
754
4
    if (pos == -1) {
755
0
      throw new KeyError();
756
4
    } else {
757
4
      return items_[pos].second;
758
4
    }
759
4
  }
Unexecuted instantiation: _ZN4DictIP3StrPN12runtime_asdl7value_tEE6index_ES1_
760
761
  // Get a key.
762
  // Returns nullptr if not found (Can't use this for non-pointer types?)
763
2
  V get(K key) {
764
2
    int pos = find(key);
765
2
    if (pos == -1) {
766
1
      return nullptr;
767
1
    } else {
768
1
      return items_[pos].second;
769
1
    }
770
2
  }
_ZN4DictIiP3StrE3getEi
Line
Count
Source
763
2
  V get(K key) {
764
2
    int pos = find(key);
765
2
    if (pos == -1) {
766
1
      return nullptr;
767
1
    } else {
768
1
      return items_[pos].second;
769
1
    }
770
2
  }
Unexecuted instantiation: _ZN4DictIP3StrS1_E3getES1_
771
772
  // Get a key, but return a default if not found.
773
  // expr_parse.py uses this with OTHER_BALANCE
774
  V get(K key, V default_val) {
775
    int pos = find(key);
776
    if (pos == -1) {
777
      return default_val;
778
    } else {
779
      return items_[pos].second;
780
    }
781
  }
782
783
  // d->set(key, val) is like (*d)[key] = val;
784
20
  void set(K key, V val) {
785
20
    int pos = find(key);
786
20
    if (pos == -1) {
787
20
      items_.push_back(std::make_pair(key, val));
788
20
    } else {
789
0
      items_[pos].second = val;
790
0
    }
791
20
  }
_ZN4DictIiP3StrE3setEiS1_
Line
Count
Source
784
1
  void set(K key, V val) {
785
1
    int pos = find(key);
786
1
    if (pos == -1) {
787
1
      items_.push_back(std::make_pair(key, val));
788
1
    } else {
789
0
      items_[pos].second = val;
790
0
    }
791
1
  }
_ZN4DictIP3StriE3setES1_i
Line
Count
Source
784
6
  void set(K key, V val) {
785
6
    int pos = find(key);
786
6
    if (pos == -1) {
787
6
      items_.push_back(std::make_pair(key, val));
788
6
    } else {
789
0
      items_[pos].second = val;
790
0
    }
791
6
  }
_ZN4DictIP3StrS1_E3setES1_S1_
Line
Count
Source
784
1
  void set(K key, V val) {
785
1
    int pos = find(key);
786
1
    if (pos == -1) {
787
1
      items_.push_back(std::make_pair(key, val));
788
1
    } else {
789
0
      items_[pos].second = val;
790
0
    }
791
1
  }
_ZN4DictIP3StrPN12runtime_asdl7value_tEE3setES1_S4_
Line
Count
Source
784
12
  void set(K key, V val) {
785
12
    int pos = find(key);
786
12
    if (pos == -1) {
787
12
      items_.push_back(std::make_pair(key, val));
788
12
    } else {
789
0
      items_[pos].second = val;
790
0
    }
791
12
  }
792
793
2
  void remove(K key) {
794
2
    mylib::dict_remove(this, key);
795
2
  }
_ZN4DictIP3StriE6removeES1_
Line
Count
Source
793
1
  void remove(K key) {
794
1
    mylib::dict_remove(this, key);
795
1
  }
_ZN4DictIP3StrS1_E6removeES1_
Line
Count
Source
793
1
  void remove(K key) {
794
1
    mylib::dict_remove(this, key);
795
1
  }
796
797
4
  List<K>* keys() {
798
4
    return dict_keys(items_);
799
4
  }
_ZN4DictIP3StriE4keysEv
Line
Count
Source
797
3
  List<K>* keys() {
798
3
    return dict_keys(items_);
799
3
  }
_ZN4DictIP3StrS1_E4keysEv
Line
Count
Source
797
1
  List<K>* keys() {
798
1
    return dict_keys(items_);
799
1
  }
800
801
  // For AssocArray transformations
802
2
  List<V>* values() {
803
2
    return dict_values(items_);
804
2
  }
_ZN4DictIP3StriE6valuesEv
Line
Count
Source
802
1
  List<V>* values() {
803
1
    return dict_values(items_);
804
1
  }
_ZN4DictIP3StrS1_E6valuesEv
Line
Count
Source
802
1
  List<V>* values() {
803
1
    return dict_values(items_);
804
1
  }
805
806
1
  void clear() {
807
1
    items_.clear();
808
1
  }
809
810
  // std::unordered_map<K, V> m_;
811
  std::vector<std::pair<K, V>> items_;
812
813
 private:
814
  // returns the position in the array
815
27
  int find(K key) {
816
27
    return find_by_key(items_, key);
817
27
  }
_ZN4DictIiP3StrE4findEi
Line
Count
Source
815
4
  int find(K key) {
816
4
    return find_by_key(items_, key);
817
4
  }
_ZN4DictIP3StriE4findES1_
Line
Count
Source
815
10
  int find(K key) {
816
10
    return find_by_key(items_, key);
817
10
  }
_ZN4DictIP3StrS1_E4findES1_
Line
Count
Source
815
1
  int find(K key) {
816
1
    return find_by_key(items_, key);
817
1
  }
_ZN4DictIP3StrPN12runtime_asdl7value_tEE4findES1_
Line
Count
Source
815
12
  int find(K key) {
816
12
    return find_by_key(items_, key);
817
12
  }
818
};
819
820
template <typename K, typename V>
821
Dict<K, V>* NewDict() {
822
  auto self = gc_heap::Alloc<Dict<K, V>>();
823
  return self;
824
}
825
826
template <typename K, typename V>
827
Dict<K, V>* NewDict(std::initializer_list<K> keys,
828
                    std::initializer_list<V> values) {
829
  // TODO(Jesse): Is this NotImplemented() or InvalidCodePath() ?
830
  assert(0);  // Uncalled
831
}
832
833
#endif  // MYLIB_LEAKY
834
835
template <class A, class B>
836
class Tuple2 {
837
 public:
838
20
  Tuple2(A a, B b) : a_(a), b_(b) {
839
20
  }
_ZN6Tuple2IiiEC2Eii
Line
Count
Source
838
9
  Tuple2(A a, B b) : a_(a), b_(b) {
839
9
  }
_ZN6Tuple2IiP3StrEC2EiS1_
Line
Count
Source
838
7
  Tuple2(A a, B b) : a_(a), b_(b) {
839
7
  }
_ZN6Tuple2IP3StrS1_EC2ES1_S1_
Line
Count
Source
838
4
  Tuple2(A a, B b) : a_(a), b_(b) {
839
4
  }
Unexecuted instantiation: _ZN6Tuple2IPN4args11_AttributesEPNS0_6ReaderEEC2ES2_S4_
840
20
  A at0() {
841
20
    return a_;
842
20
  }
_ZN6Tuple2IP3StrS1_E3at0Ev
Line
Count
Source
840
4
  A at0() {
841
4
    return a_;
842
4
  }
_ZN6Tuple2IiiE3at0Ev
Line
Count
Source
840
9
  A at0() {
841
9
    return a_;
842
9
  }
_ZN6Tuple2IiP3StrE3at0Ev
Line
Count
Source
840
7
  A at0() {
841
7
    return a_;
842
7
  }
Unexecuted instantiation: _ZN6Tuple2IP3StriE3at0Ev
843
19
  B at1() {
844
19
    return b_;
845
19
  }
_ZN6Tuple2IP3StrS1_E3at1Ev
Line
Count
Source
843
4
  B at1() {
844
4
    return b_;
845
4
  }
_ZN6Tuple2IiiE3at1Ev
Line
Count
Source
843
9
  B at1() {
844
9
    return b_;
845
9
  }
_ZN6Tuple2IiP3StrE3at1Ev
Line
Count
Source
843
6
  B at1() {
844
6
    return b_;
845
6
  }
Unexecuted instantiation: _ZN6Tuple2IP3StriE3at1Ev
846
847
 private:
848
  A a_;
849
  B b_;
850
};
851
852
template <class A, class B, class C>
853
class Tuple3 {
854
 public:
855
1
  Tuple3(A a, B b, C c) : a_(a), b_(b), c_(c) {
856
1
  }
_ZN6Tuple3IiP3StrS1_EC2EiS1_S1_
Line
Count
Source
855
1
  Tuple3(A a, B b, C c) : a_(a), b_(b), c_(c) {
856
1
  }
Unexecuted instantiation: _ZN6Tuple3IdddEC2Eddd
857
1
  A at0() {
858
1
    return a_;
859
1
  }
860
1
  B at1() {
861
1
    return b_;
862
1
  }
863
1
  C at2() {
864
1
    return c_;
865
1
  }
866
867
 private:
868
  A a_;
869
  B b_;
870
  C c_;
871
};
872
873
template <class A, class B, class C, class D>
874
class Tuple4 {
875
 public:
876
1
  Tuple4(A a, B b, C c, D d) : a_(a), b_(b), c_(c), d_(d) {
877
1
  }
878
1
  A at0() {
879
1
    return a_;
880
1
  }
881
1
  B at1() {
882
1
    return b_;
883
1
  }
884
1
  C at2() {
885
1
    return c_;
886
1
  }
887
1
  D at3() {
888
1
    return d_;
889
1
  }
890
891
 private:
892
  A a_;
893
  B b_;
894
  C c_;
895
  D d_;
896
};
897
898
//
899
// Overloaded free function len()
900
//
901
902
#ifdef MYLIB_LEAKY
903
23
inline int len(const Str* s) {
904
23
  return s->len_;
905
23
}
906
907
template <typename T>
908
30
inline int len(const List<T>* L) {
909
  // inline int len(List<T>* L) {
910
30
  return L->v_.size();
911
30
}
_Z3lenIiEiPK4ListIT_E
Line
Count
Source
908
17
inline int len(const List<T>* L) {
909
  // inline int len(List<T>* L) {
910
17
  return L->v_.size();
911
17
}
_Z3lenIbEiPK4ListIT_E
Line
Count
Source
908
1
inline int len(const List<T>* L) {
909
  // inline int len(List<T>* L) {
910
1
  return L->v_.size();
911
1
}
_Z3lenIP3StrEiPK4ListIT_E
Line
Count
Source
908
12
inline int len(const List<T>* L) {
909
  // inline int len(List<T>* L) {
910
12
  return L->v_.size();
911
12
}
912
913
template <typename K, typename V>
914
inline int len(const Dict<K, V>* d) {
915
  assert(0);
916
}
917
918
template <typename V>
919
7
inline int len(const Dict<Str*, V>* d) {
920
7
  int len = 0;
921
7
  int n = d->items_.size();
922
21
  for (int i = 0; i < n; ++i) {
923
14
    Str* s = d->items_[i].first;  // nullptr for deleted entries
924
14
    if (s) {
925
10
      len++;
926
10
    }
927
14
  }
928
7
  return len;
929
7
}
_Z3lenIiEiPK4DictIP3StrT_E
Line
Count
Source
919
5
inline int len(const Dict<Str*, V>* d) {
920
5
  int len = 0;
921
5
  int n = d->items_.size();
922
17
  for (int i = 0; i < n; ++i) {
923
12
    Str* s = d->items_[i].first;  // nullptr for deleted entries
924
12
    if (s) {
925
9
      len++;
926
9
    }
927
12
  }
928
5
  return len;
929
5
}
_Z3lenIP3StrEiPK4DictIS1_T_E
Line
Count
Source
919
2
inline int len(const Dict<Str*, V>* d) {
920
2
  int len = 0;
921
2
  int n = d->items_.size();
922
4
  for (int i = 0; i < n; ++i) {
923
2
    Str* s = d->items_[i].first;  // nullptr for deleted entries
924
2
    if (s) {
925
1
      len++;
926
1
    }
927
2
  }
928
2
  return len;
929
2
}
930
#endif
931
932
//
933
// Free functions
934
//
935
936
Str* str_concat(Str* a, Str* b);           // a + b when a and b are strings
937
Str* str_concat3(Str* a, Str* b, Str* c);  // for os_path::join()
938
939
Str* str_repeat(Str* s, int times);  // e.g. ' ' * 3
940
941
#if MYLIB_LEAKY
942
132
inline bool str_equals(Str* left, Str* right) {
943
132
  if (left->len_ == right->len_) {
944
127
    return memcmp(left->data_, right->data_, left->len_) == 0;
945
127
  } else {
946
5
    return false;
947
5
  }
948
132
}
949
950
namespace id_kind_asdl {
951
enum class Kind;
952
};
953
954
inline bool are_equal(id_kind_asdl::Kind left, id_kind_asdl::Kind right);
955
956
8
inline bool are_equal(int left, int right) {
957
8
  return left == right;
958
0
  ;
959
0
}
960
961
9
inline bool are_equal(Str* left, Str* right) {
962
9
  return str_equals(left, right);
963
9
}
964
965
0
inline bool are_equal(Tuple2<Str*, int>* t1, Tuple2<Str*, int>* t2) {
966
0
  bool result = are_equal(t1->at0(), t2->at0());
967
0
  result = result && (t1->at1() == t2->at1());
968
0
  return result;
969
0
}
970
#endif
971
972
94
inline bool str_equals0(const char* c_string, Str* s) {
973
94
  int n = strlen(c_string);
974
94
  if (s->len_ == n) {
975
14
    return memcmp(s->data_, c_string, n) == 0;
976
80
  } else {
977
80
    return false;
978
80
  }
979
94
}
980
981
0
inline bool maybe_str_equals(Str* left, Str* right) {
982
0
  if (left && right) {
983
0
    return str_equals(left, right);
984
0
  }
985
0
986
0
  if (!left && !right) {
987
0
    return true;  // None == None
988
0
  }
989
0
990
0
  return false;  // one is None and one is a Str*
991
0
}
992
993
inline Str* chr(int i) {
994
  char* buf = static_cast<char*>(malloc(2));
995
  buf[0] = i;
996
  buf[1] = '\0';
997
  return new Str(buf, 1);
998
}
999
1000
0
inline int ord(Str* s) {
1001
0
  assert(s->len_ == 1);
1002
0
  // signed to unsigned conversion, so we don't get values like -127
1003
0
  uint8_t c = static_cast<uint8_t>(s->data_[0]);
1004
0
  return c;
1005
0
}
1006
1007
// https://stackoverflow.com/questions/3919995/determining-sprintf-buffer-size-whats-the-standard/11092994#11092994
1008
// Notes:
1009
// - Python 2.7's intobject.c has an erroneous +6
1010
// - This is 13, but len('-2147483648') is 11, which means we only need 12?
1011
// - This formula is valid for octal(), because 2^(3 bits) = 8
1012
const int kIntBufSize = CHAR_BIT * sizeof(int) / 3 + 3;
1013
1014
inline Str* str(int i) {
1015
  char* buf = static_cast<char*>(malloc(kIntBufSize));
1016
  int len = snprintf(buf, kIntBufSize, "%d", i);
1017
  return new Str(buf, len);
1018
}
1019
1020
0
inline Str* str(double f) {  // TODO: should be double
1021
0
  NotImplemented();          // Uncalled
1022
0
}
1023
1024
// Display a quoted representation of a string.  word_.Pretty() uses it.
1025
Str* repr(Str* s);
1026
1027
// TODO: There should be one str() and one repr() for every sum type, that
1028
// dispatches on tag?  Or just repr()?
1029
1030
// Will need it for dict, but not tuple.
1031
// inline int len(Dict* D) {
1032
// }
1033
1034
bool _str_to_int(Str* s, int* result, int base);  // for testing only
1035
int to_int(Str* s);
1036
int to_int(Str* s, int base);
1037
1038
// int(a == b) used in arithmetic evaluator
1039
0
inline int to_int(bool b) {
1040
0
  return b;
1041
0
}
1042
1043
inline bool to_bool(int i) {
1044
  return i != 0;
1045
}
1046
1047
0
inline bool to_bool(Str* s) {
1048
0
  return s->len_ != 0;
1049
0
}
1050
1051
0
inline double to_float(Str* s) {
1052
0
  assert(s->IsNulTerminated());
1053
0
  double result = atof(s->data_);
1054
0
  return result;
1055
0
}
1056
1057
// e.g. ('a' in 'abc')
1058
8
inline bool str_contains(Str* haystack, Str* needle) {
1059
  // Common case
1060
8
  if (needle->len_ == 1) {
1061
2
    return memchr(haystack->data_, needle->data_[0], haystack->len_);
1062
2
  }
1063
1064
  // General case. TODO: We could use a smarter substring algorithm.
1065
6
  if (needle->len_ > haystack->len_) {
1066
1
    return false;
1067
1
  }
1068
1069
5
  const char* end = haystack->data_ + haystack->len_;
1070
5
  const char* last_possible = end - needle->len_;
1071
5
  const char* p = haystack->data_;
1072
1073
11
  while (p <= last_possible) {
1074
10
    if (memcmp(p, needle->data_, needle->len_) == 0) {
1075
4
      return true;
1076
4
    }
1077
6
    p++;
1078
6
  }
1079
1
  return false;
1080
5
}
1081
1082
// e.g. [None] * 3
1083
template <typename T>
1084
2
List<T>* list_repeat(T item, int times) {
1085
2
  return new List<T>(item, times);
1086
2
}
_Z11list_repeatIP3StrEP4ListIT_ES3_i
Line
Count
Source
1084
1
List<T>* list_repeat(T item, int times) {
1085
1
  return new List<T>(item, times);
1086
1
}
_Z11list_repeatIbEP4ListIT_ES1_i
Line
Count
Source
1084
1
List<T>* list_repeat(T item, int times) {
1085
1
  return new List<T>(item, times);
1086
1
}
1087
1088
// list(L) copies the list
1089
template <typename T>
1090
List<T>* list(List<T>* other) {
1091
  auto result = new List<T>();
1092
  for (int i = 0; i < len(other); ++i) {
1093
    result->set(i, other->index_(i));
1094
  }
1095
  return result;
1096
}
1097
1098
template <typename T>
1099
8
bool list_contains(List<T>* haystack, T needle) {
1100
8
  int n = haystack->v_.size();
1101
21
  for (int i = 0; i < n; ++i) {
1102
17
    if (are_equal(haystack->index_(i), needle)) {
1103
4
      return true;
1104
4
    }
1105
17
  }
1106
4
  return false;
1107
8
}
_Z13list_containsIP3StrEbP4ListIT_ES3_
Line
Count
Source
1099
4
bool list_contains(List<T>* haystack, T needle) {
1100
4
  int n = haystack->v_.size();
1101
11
  for (int i = 0; i < n; ++i) {
1102
9
    if (are_equal(haystack->index_(i), needle)) {
1103
2
      return true;
1104
2
    }
1105
9
  }
1106
2
  return false;
1107
4
}
_Z13list_containsIiEbP4ListIT_ES1_
Line
Count
Source
1099
2
bool list_contains(List<T>* haystack, T needle) {
1100
2
  int n = haystack->v_.size();
1101
5
  for (int i = 0; i < n; ++i) {
1102
4
    if (are_equal(haystack->index_(i), needle)) {
1103
1
      return true;
1104
1
    }
1105
4
  }
1106
1
  return false;
1107
2
}
_Z13list_containsIdEbP4ListIT_ES1_
Line
Count
Source
1099
2
bool list_contains(List<T>* haystack, T needle) {
1100
2
  int n = haystack->v_.size();
1101
5
  for (int i = 0; i < n; ++i) {
1102
4
    if (are_equal(haystack->index_(i), needle)) {
1103
1
      return true;
1104
1
    }
1105
4
  }
1106
1
  return false;
1107
2
}
1108
1109
template <typename T>
1110
bool list_contains(List<T>* haystack, T* needle) {
1111
  bool result = false;
1112
1113
  if (needle) {
1114
    result = list_contains(haystack, *needle);
1115
  }
1116
1117
  return result;
1118
}
1119
1120
template <typename K, typename V>
1121
5
inline bool dict_contains(Dict<K, V>* haystack, K needle) {
1122
5
  return find_by_key(haystack->items_, needle) != -1;
1123
5
}
_Z13dict_containsIiP3StrEbP4DictIT_T0_ES3_
Line
Count
Source
1121
2
inline bool dict_contains(Dict<K, V>* haystack, K needle) {
1122
2
  return find_by_key(haystack->items_, needle) != -1;
1123
2
}
_Z13dict_containsIP3StriEbP4DictIT_T0_ES3_
Line
Count
Source
1121
3
inline bool dict_contains(Dict<K, V>* haystack, K needle) {
1122
3
  return find_by_key(haystack->items_, needle) != -1;
1123
3
}
1124
1125
template <typename V>
1126
1
List<Str*>* sorted(Dict<Str*, V>* d) {
1127
1
  auto keys = d->keys();
1128
1
  keys->sort();
1129
1
  return keys;
1130
1
}
1131
1132
8
inline int int_cmp(int a, int b) {
1133
8
  if (a == b) {
1134
2
    return 0;
1135
2
  }
1136
6
  return a < b ? -1 : 1;
1137
8
}
1138
1139
// Used by [[ a > b ]] and so forth
1140
14
inline int str_cmp(Str* a, Str* b) {
1141
14
  int min = std::min(a->len_, b->len_);
1142
14
  if (min == 0) {
1143
4
    return int_cmp(a->len_, b->len_);
1144
4
  }
1145
10
  int comp = memcmp(a->data_, b->data_, min);
1146
10
  if (comp == 0) {
1147
1
    return int_cmp(a->len_, b->len_);  // tiebreaker
1148
1
  }
1149
9
  return comp;
1150
10
}
1151
1152
// Hm std::sort() just needs true/false, not 0, 1, 1.
1153
8
inline bool _cmp(Str* a, Str* b) {
1154
8
  return str_cmp(a, b) < 0;
1155
8
}
1156
1157
// specialization for Str only
1158
2
inline void mysort(std::vector<Str*>* v) {
1159
2
  std::sort(v->begin(), v->end(), _cmp);
1160
2
}
1161
1162
//
1163
// Buf is StringIO
1164
//
1165
1166
namespace mylib {  // MyPy artifact
1167
1168
template <typename V>
1169
3
inline void dict_remove(Dict<Str*, V>* haystack, Str* needle) {
1170
3
  int pos = find_by_key(haystack->items_, needle);
1171
3
  if (pos == -1) {
1172
0
    return;
1173
0
  }
1174
3
  haystack->items_[pos].first = nullptr;
1175
3
}
_ZN5mylib11dict_removeIiEEvP4DictIP3StrT_ES3_
Line
Count
Source
1169
2
inline void dict_remove(Dict<Str*, V>* haystack, Str* needle) {
1170
2
  int pos = find_by_key(haystack->items_, needle);
1171
2
  if (pos == -1) {
1172
0
    return;
1173
0
  }
1174
2
  haystack->items_[pos].first = nullptr;
1175
2
}
_ZN5mylib11dict_removeIP3StrEEvP4DictIS2_T_ES2_
Line
Count
Source
1169
1
inline void dict_remove(Dict<Str*, V>* haystack, Str* needle) {
1170
1
  int pos = find_by_key(haystack->items_, needle);
1171
1
  if (pos == -1) {
1172
0
    return;
1173
0
  }
1174
1
  haystack->items_[pos].first = nullptr;
1175
1
}
1176
1177
// TODO: how to do the int version of this?  Do you need an extra bit?
1178
template <typename V>
1179
inline void dict_remove(Dict<int, V>* haystack, int needle) {
1180
  NotImplemented();
1181
}
1182
1183
// A class for interfacing Str* slices with C functions that expect a NUL
1184
// terminated string.  It's meant to be used on the stack, like
1185
//
1186
// void f(Str* pat) {
1187
//   Str0 c_pattern(pat);
1188
//   int n = strlen(c_pattern.Get());
1189
//
1190
//   // copy of Str* is destroyed
1191
// }
1192
class Str0 {
1193
 public:
1194
47
  Str0(Str* s) : s_(s), nul_str_(nullptr) {
1195
47
  }
1196
94
  ~Str0() {
1197
94
    if (nul_str_) {
1198
6
      free(nul_str_);
1199
6
    }
1200
94
  }
_ZN5mylib4Str0D2Ev
Line
Count
Source
1196
47
  ~Str0() {
1197
47
    if (nul_str_) {
1198
3
      free(nul_str_);
1199
3
    }
1200
47
  }
_ZN5mylib4Str0D2Ev
Line
Count
Source
1196
47
  ~Str0() {
1197
47
    if (nul_str_) {
1198
3
      free(nul_str_);
1199
3
    }
1200
47
  }
1201
1202
94
  const char* Get() {  // caller should not modify this string!
1203
94
    if (s_->IsNulTerminated()) {
1204
88
      return s_->data_;
1205
88
    } else {
1206
6
      nul_str_ = static_cast<char*>(malloc(s_->len_ + 1));
1207
6
      memcpy(nul_str_, s_->data_, s_->len_);
1208
6
      nul_str_[s_->len_] = '\0';
1209
6
      return nul_str_;
1210
6
    }
1211
94
  }
_ZN5mylib4Str03GetEv
Line
Count
Source
1202
47
  const char* Get() {  // caller should not modify this string!
1203
47
    if (s_->IsNulTerminated()) {
1204
44
      return s_->data_;
1205
44
    } else {
1206
3
      nul_str_ = static_cast<char*>(malloc(s_->len_ + 1));
1207
3
      memcpy(nul_str_, s_->data_, s_->len_);
1208
3
      nul_str_[s_->len_] = '\0';
1209
3
      return nul_str_;
1210
3
    }
1211
47
  }
_ZN5mylib4Str03GetEv
Line
Count
Source
1202
47
  const char* Get() {  // caller should not modify this string!
1203
47
    if (s_->IsNulTerminated()) {
1204
44
      return s_->data_;
1205
44
    } else {
1206
3
      nul_str_ = static_cast<char*>(malloc(s_->len_ + 1));
1207
3
      memcpy(nul_str_, s_->data_, s_->len_);
1208
3
      nul_str_[s_->len_] = '\0';
1209
3
      return nul_str_;
1210
3
    }
1211
47
  }
1212
1213
  Str* s_;
1214
  char* nul_str_;
1215
};
1216
1217
Tuple2<Str*, Str*> split_once(Str* s, Str* delim);
1218
1219
// Emulate GC API so we can reuse bindings
1220
1221
4
inline Str* AllocStr(int len) {
1222
4
  char* buf = static_cast<char*>(malloc(len + 1));
1223
4
  memset(buf, 0, len + 1);
1224
4
  return new Str(buf, len);
1225
4
}
_ZN5mylib8AllocStrEi
Line
Count
Source
1221
2
inline Str* AllocStr(int len) {
1222
2
  char* buf = static_cast<char*>(malloc(len + 1));
1223
2
  memset(buf, 0, len + 1);
1224
2
  return new Str(buf, len);
1225
2
}
_ZN5mylib8AllocStrEi
Line
Count
Source
1221
2
inline Str* AllocStr(int len) {
1222
2
  char* buf = static_cast<char*>(malloc(len + 1));
1223
2
  memset(buf, 0, len + 1);
1224
2
  return new Str(buf, len);
1225
2
}
1226
1227
1
inline Str* OverAllocatedStr(int len) {
1228
  // Here they are identical, but in gc_heap.cc they're different
1229
1
  return AllocStr(len);
1230
1
}
1231
1232
4
inline Str* CopyStr(const char* s, int len) {
1233
  // take ownership (but still leaks)
1234
4
  char* buf = static_cast<char*>(malloc(len + 1));
1235
4
  memcpy(buf, s, len);
1236
4
  buf[len] = '\0';
1237
4
  return new Str(buf, len);
1238
4
}
_ZN5mylib7CopyStrEPKci
Line
Count
Source
1232
2
inline Str* CopyStr(const char* s, int len) {
1233
  // take ownership (but still leaks)
1234
2
  char* buf = static_cast<char*>(malloc(len + 1));
1235
2
  memcpy(buf, s, len);
1236
2
  buf[len] = '\0';
1237
2
  return new Str(buf, len);
1238
2
}
_ZN5mylib7CopyStrEPKci
Line
Count
Source
1232
2
inline Str* CopyStr(const char* s, int len) {
1233
  // take ownership (but still leaks)
1234
2
  char* buf = static_cast<char*>(malloc(len + 1));
1235
2
  memcpy(buf, s, len);
1236
2
  buf[len] = '\0';
1237
2
  return new Str(buf, len);
1238
2
}
1239
1240
1
inline Str* CopyStr(const char* s) {
1241
1
  return CopyStr(s, strlen(s));
1242
1
}
1243
1244
// emulate gc_heap API for ASDL
1245
1246
template <typename T>
1247
0
List<T>* NewList() {
1248
0
  return new List<T>();
1249
0
}
Unexecuted instantiation: _ZN5mylib7NewListIPN10hnode_asdl5fieldEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN10hnode_asdl7hnode_tEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIP3StrEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIiEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN12runtime_asdl10assign_argEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN12runtime_asdl12part_value_tEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN12runtime_asdl7value_tEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl13compound_wordEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl11word_part_tEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl9command_tEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl6word_tEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl5redirEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl8env_pairEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl11assign_pairEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl6if_armEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl8case_armEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl9name_typeEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl12place_expr_tEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl5paramEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl11type_expr_tEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl7variantEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl12class_item_tEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl11import_nameEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl12UntypedParamEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl10TypedParamEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl5TokenEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl5speckEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl6expr_tEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl13comprehensionEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl20class_literal_term_tEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl4re_tEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl9named_argEEEP4ListIT_Ev
Unexecuted instantiation: _ZN5mylib7NewListIPN11syntax_asdl11string_lineEEEP4ListIT_Ev
1250
1251
template <typename T>
1252
List<T>* NewList(std::initializer_list<T> init) {
1253
  return new List<T>(init);
1254
}
1255
1256
class LineReader {
1257
 public:
1258
  virtual Str* readline() = 0;
1259
1
  virtual bool isatty() {
1260
1
    return false;
1261
1
  }
1262
1263
0
  virtual int fileno() {
1264
0
    NotImplemented();
1265
0
  }
1266
};
1267
1268
class BufLineReader : public LineReader {
1269
 public:
1270
0
  explicit BufLineReader(Str* s) : s_(s), pos_(s->data_) {
1271
0
  }
1272
  virtual Str* readline();
1273
1274
 private:
1275
  Str* s_;
1276
  const char* pos_;
1277
1278
  DISALLOW_COPY_AND_ASSIGN(BufLineReader)
1279
};
1280
1281
// Wrap a FILE*
1282
class CFileLineReader : public LineReader {
1283
 public:
1284
4
  explicit CFileLineReader(FILE* f) : f_(f) {
1285
4
  }
1286
  virtual Str* readline();
1287
1
  virtual int fileno() {
1288
1
    return ::fileno(f_);
1289
1
  }
1290
1291
 private:
1292
  FILE* f_;
1293
1294
  DISALLOW_COPY_AND_ASSIGN(CFileLineReader)
1295
};
1296
1297
extern LineReader* gStdin;
1298
1299
inline LineReader* Stdin() {
1300
  if (gStdin == nullptr) {
1301
    gStdin = new CFileLineReader(stdin);
1302
  }
1303
  return gStdin;
1304
}
1305
1306
0
inline LineReader* open(Str* path) {
1307
0
  Str0 path0(path);
1308
0
  FILE* f = fopen(path0.Get(), "r");
1309
0
1310
0
  // TODO: Better error checking.  IOError?
1311
0
  if (!f) {
1312
0
    throw new AssertionError("file not found");
1313
0
  }
1314
0
  return new CFileLineReader(f);
1315
0
}
1316
1317
class Writer {
1318
 public:
1319
  virtual void write(Str* s) = 0;
1320
  virtual void flush() = 0;
1321
  virtual bool isatty() = 0;
1322
};
1323
1324
class BufWriter : public Writer {
1325
 public:
1326
60
  BufWriter() : data_(nullptr), len_(0) {
1327
60
  }
1328
  virtual void write(Str* s) override;
1329
0
  virtual void flush() override {
1330
0
  }
1331
0
  virtual bool isatty() override {
1332
0
    return false;
1333
0
  }
1334
  // For cStringIO API
1335
317
  Str* getvalue() {
1336
317
    if (data_) {
1337
317
      Str* ret = new Str(data_, len_);
1338
317
      reset();  // Invalidate this instance
1339
317
      return ret;
1340
317
    } else {
1341
      // log('') translates to this
1342
      // Strings are immutable so we can do this.
1343
0
      return kEmptyString;
1344
0
    }
1345
317
  }
1346
1347
  // Methods to compile printf format strings to
1348
1349
  // To reuse the global gBuf instance
1350
  // problem: '%r' % obj will recursively call asdl/format.py, which has its
1351
  // own % operations
1352
610
  void reset() {
1353
610
    data_ = nullptr;  // make sure we get a new buffer next time
1354
610
    len_ = 0;
1355
610
  }
1356
1357
  // Note: we do NOT need to instantiate a Str() to append
1358
  void write_const(const char* s, int len);
1359
1360
  // strategy: snprintf() based on sizeof(int)
1361
  void format_d(int i);
1362
  void format_o(int i);
1363
  void format_s(Str* s);
1364
  void format_r(Str* s);  // formats with quotes
1365
1366
  // looks at arbitrary type tags?  Is this possible
1367
  // Passes "this" to functions generated by ASDL?
1368
  void format_r(void* s);
1369
1370
 private:
1371
  // Just like a string, except it's mutable
1372
  char* data_;
1373
  int len_;
1374
};
1375
1376
// Wrap a FILE*
1377
class CFileWriter : public Writer {
1378
 public:
1379
  explicit CFileWriter(FILE* f) : f_(f) {
1380
  }
1381
  virtual void write(Str* s) override;
1382
  virtual void flush() override;
1383
  virtual bool isatty() override;
1384
1385
 private:
1386
  FILE* f_;
1387
1388
  DISALLOW_COPY_AND_ASSIGN(CFileWriter)
1389
};
1390
1391
extern Writer* gStdout;
1392
1393
inline Writer* Stdout() {
1394
  if (gStdout == nullptr) {
1395
    gStdout = new CFileWriter(stdout);
1396
  }
1397
  return gStdout;
1398
}
1399
1400
extern Writer* gStderr;
1401
1402
0
inline Writer* Stderr() {
1403
0
  if (gStderr == nullptr) {
1404
0
    gStderr = new CFileWriter(stderr);
1405
0
  }
1406
0
  return gStderr;
1407
0
}
1408
1409
4
inline Str* hex_lower(int i) {
1410
4
  char* buf = static_cast<char*>(malloc(kIntBufSize));
1411
4
  int len = snprintf(buf, kIntBufSize, "%x", i);
1412
4
  return new Str(buf, len);
1413
4
}
1414
1415
4
inline Str* hex_upper(int i) {
1416
4
  char* buf = static_cast<char*>(malloc(kIntBufSize));
1417
4
  int len = snprintf(buf, kIntBufSize, "%X", i);
1418
4
  return new Str(buf, len);
1419
4
}
1420
1421
4
inline Str* octal(int i) {
1422
4
  char* buf = static_cast<char*>(malloc(kIntBufSize));
1423
4
  int len = snprintf(buf, kIntBufSize, "%o", i);
1424
4
  return new Str(buf, len);
1425
4
}
1426
1427
}  // namespace mylib
1428
1429
//
1430
// Formatter for Python's %s
1431
//
1432
1433
extern mylib::BufWriter gBuf;
1434
1435
// mycpp doesn't understand dynamic format strings yet
1436
0
inline Str* dynamic_fmt_dummy() {
1437
0
  return new Str("dynamic_fmt_dummy");
1438
0
}
1439
1440
#endif  // MYLIB_H