1// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/compiler/backend/sexpression.h"
6
7#include <ctype.h>
8#include "platform/utils.h"
9#include "vm/double_conversion.h"
10#include "vm/zone_text_buffer.h"
11
12namespace dart {
13
14SExpression* SExpression::FromCString(Zone* zone, const char* str) {
15 SExpParser parser(zone, str);
16 auto sexp = parser.Parse();
17 if (sexp == nullptr) parser.ReportError();
18 return sexp;
19}
20
21const char* SExpression::ToCString(Zone* zone) const {
22 ZoneTextBuffer buf(zone, 1 * KB);
23 SerializeToLine(&buf);
24 return buf.buffer();
25}
26
27bool SExpBool::Equals(SExpression* sexp) const {
28 if (auto const b = sexp->AsBool()) return b->Equals(value());
29 return false;
30}
31
32bool SExpBool::Equals(bool val) const {
33 return value() == val;
34}
35
36void SExpBool::SerializeToLine(BaseTextBuffer* buffer) const {
37 buffer->AddString(value() ? SExpParser::kBoolTrueSymbol
38 : SExpParser::kBoolFalseSymbol);
39}
40
41bool SExpDouble::Equals(SExpression* sexp) const {
42 if (auto const d = sexp->AsDouble()) return d->Equals(value());
43 return false;
44}
45
46bool SExpDouble::Equals(double val) const {
47 return value() == val;
48}
49
50void SExpDouble::SerializeToLine(BaseTextBuffer* buffer) const {
51 // Use existing Dart serialization for Doubles.
52 const intptr_t kBufSize = 128;
53 char strbuf[kBufSize];
54 DoubleToCString(value(), strbuf, kBufSize);
55 buffer->Printf("%s", strbuf);
56}
57
58bool SExpInteger::Equals(SExpression* sexp) const {
59 if (auto const i = sexp->AsInteger()) return i->Equals(value());
60 return false;
61}
62
63bool SExpInteger::Equals(int64_t val) const {
64 return value() == val;
65}
66
67void SExpInteger::SerializeToLine(BaseTextBuffer* buffer) const {
68 buffer->Printf("%" Pd64 "", value());
69}
70
71bool SExpString::Equals(SExpression* sexp) const {
72 if (auto const s = sexp->AsString()) return s->Equals(value());
73 return false;
74}
75
76bool SExpString::Equals(const char* str) const {
77 return strcmp(value(), str) == 0;
78}
79
80void SExpString::SerializeToLine(BaseTextBuffer* buffer) const {
81 TextBuffer buf(80);
82 buf.AddChar('"');
83 buf.AddEscapedString(value());
84 buf.AddChar('"');
85 buffer->AddString(buf.buffer());
86}
87
88bool SExpSymbol::Equals(SExpression* sexp) const {
89 if (auto const s = sexp->AsSymbol()) return s->Equals(value());
90 return false;
91}
92
93bool SExpSymbol::Equals(const char* str) const {
94 return strcmp(value(), str) == 0;
95}
96
97void SExpSymbol::SerializeToLine(BaseTextBuffer* buffer) const {
98 buffer->AddString(value());
99}
100
101void SExpList::Add(SExpression* sexp) {
102 contents_.Add(sexp);
103}
104
105void SExpList::AddExtra(const char* label, SExpression* value) {
106 ASSERT(!extra_info_.HasKey(label));
107 extra_info_.Insert({label, value});
108}
109
110bool SExpList::Equals(SExpression* sexp) const {
111 if (!sexp->IsList()) return false;
112 auto list = sexp->AsList();
113 if (Length() != list->Length()) return false;
114 if (ExtraLength() != list->ExtraLength()) return false;
115 for (intptr_t i = 0; i < Length(); i++) {
116 if (!At(i)->Equals(list->At(i))) return false;
117 }
118 auto this_it = ExtraIterator();
119 while (auto kv = this_it.Next()) {
120 if (!list->ExtraHasKey(kv->key)) return false;
121 if (!kv->value->Equals(list->ExtraLookupValue(kv->key))) return false;
122 }
123 return true;
124}
125
126const char* const SExpList::kElemIndent = " ";
127const char* const SExpList::kExtraIndent = " ";
128
129static intptr_t HandleLineBreaking(Zone* zone,
130 BaseTextBuffer* buffer,
131 SExpression* element,
132 BaseTextBuffer* line_buffer,
133 const char* sub_indent,
134 intptr_t width,
135 bool leading_space,
136 intptr_t remaining) {
137 element->SerializeToLine(line_buffer);
138 const intptr_t single_line_width = line_buffer->length();
139 const intptr_t leading_length = leading_space ? 1 : 0;
140
141 if ((leading_length + single_line_width) < remaining) {
142 if (leading_space) buffer->AddChar(' ');
143 buffer->AddString(line_buffer->buffer());
144 line_buffer->Clear();
145 return remaining - (leading_length + single_line_width);
146 }
147 const intptr_t old_length = buffer->length();
148 buffer->Printf("\n%s", sub_indent);
149 const intptr_t line_used = buffer->length() - old_length + 1;
150 remaining = width - line_used;
151 if ((single_line_width < remaining) || element->IsAtom()) {
152 buffer->AddString(line_buffer->buffer());
153 line_buffer->Clear();
154 return remaining - single_line_width;
155 }
156 line_buffer->Clear();
157 element->SerializeTo(zone, buffer, sub_indent, width);
158 return 0;
159}
160
161// Assumes that we are starting on a line after [indent] amount of space.
162void SExpList::SerializeTo(Zone* zone,
163 BaseTextBuffer* buffer,
164 const char* indent,
165 intptr_t width) const {
166 TextBuffer single_line(width);
167 const char* sub_indent = OS::SCreate(zone, "%s%s", indent, kElemIndent);
168
169 buffer->AddChar('(');
170 intptr_t remaining = width - strlen(indent) - 1;
171 for (intptr_t i = 0; i < contents_.length(); i++) {
172 remaining = HandleLineBreaking(zone, buffer, contents_.At(i), &single_line,
173 sub_indent, width, i != 0, remaining);
174 }
175
176 if (!extra_info_.IsEmpty()) {
177 SerializeExtraInfoToLine(&single_line);
178 if (single_line.length() < remaining - 1) {
179 buffer->Printf(" %s", single_line.buffer());
180 } else {
181 const intptr_t old_length = buffer->length();
182 buffer->Printf("\n%s", sub_indent);
183 const intptr_t line_used = buffer->length() - old_length + 1;
184 remaining = width - line_used;
185 if (single_line.length() < remaining) {
186 buffer->AddString(single_line.buffer());
187 } else {
188 SerializeExtraInfoTo(zone, buffer, sub_indent, width);
189 }
190 }
191 }
192 buffer->AddChar(')');
193}
194
195void SExpList::SerializeToLine(BaseTextBuffer* buffer) const {
196 buffer->AddChar('(');
197 for (intptr_t i = 0; i < contents_.length(); i++) {
198 if (i != 0) buffer->AddChar(' ');
199 contents_.At(i)->SerializeToLine(buffer);
200 }
201 if (!extra_info_.IsEmpty()) {
202 buffer->AddChar(' ');
203 SerializeExtraInfoToLine(buffer);
204 }
205 buffer->AddChar(')');
206}
207
208void SExpList::SerializeExtraInfoTo(Zone* zone,
209 BaseTextBuffer* buffer,
210 const char* indent,
211 int width) const {
212 const char* sub_indent = OS::SCreate(zone, "%s%s", indent, kExtraIndent);
213 TextBuffer single_line(width);
214
215 buffer->AddChar('{');
216 auto it = ExtraIterator();
217 while (auto kv = it.Next()) {
218 const intptr_t old_length = buffer->length();
219 buffer->Printf("\n%s%s", sub_indent, kv->key);
220 const intptr_t remaining = width - (buffer->length() - old_length + 1);
221 HandleLineBreaking(zone, buffer, kv->value, &single_line, sub_indent, width,
222 /*leading_space=*/true, remaining);
223 buffer->AddChar(',');
224 }
225 buffer->Printf("\n%s}", indent);
226}
227
228void SExpList::SerializeExtraInfoToLine(BaseTextBuffer* buffer) const {
229 buffer->AddString("{");
230 auto it = ExtraIterator();
231 while (auto kv = it.Next()) {
232 buffer->Printf(" %s ", kv->key);
233 kv->value->SerializeToLine(buffer);
234 buffer->AddChar(',');
235 }
236 buffer->AddString(" }");
237}
238
239const char* const SExpParser::kBoolTrueSymbol = "true";
240const char* const SExpParser::kBoolFalseSymbol = "false";
241char const SExpParser::kDoubleExponentChar =
242 DoubleToStringConstants::kExponentChar;
243const char* const SExpParser::kDoubleInfinitySymbol =
244 DoubleToStringConstants::kInfinitySymbol;
245const char* const SExpParser::kDoubleNaNSymbol =
246 DoubleToStringConstants::kNaNSymbol;
247
248const char* const SExpParser::ErrorStrings::kOpenString =
249 "unterminated quoted string starting at position %" Pd "";
250const char* const SExpParser::ErrorStrings::kBadUnicodeEscape =
251 "malformed Unicode escape";
252const char* const SExpParser::ErrorStrings::kOpenSExpList =
253 "unterminated S-expression list starting at position %" Pd "";
254const char* const SExpParser::ErrorStrings::kOpenMap =
255 "unterminated extra info map starting at position %" Pd "";
256const char* const SExpParser::ErrorStrings::kNestedMap =
257 "extra info map start when already within extra info map";
258const char* const SExpParser::ErrorStrings::kMapOutsideList =
259 "extra info map start not within S-expression list";
260const char* const SExpParser::ErrorStrings::kNonSymbolLabel =
261 "non-symbol in label position for extra info map";
262const char* const SExpParser::ErrorStrings::kNoMapLabel =
263 "no extra info map label provided";
264const char* const SExpParser::ErrorStrings::kRepeatedMapLabel =
265 "extra info map label %s provided more than once";
266const char* const SExpParser::ErrorStrings::kNoMapValue =
267 "no value provided for extra info map label %s";
268const char* const SExpParser::ErrorStrings::kExtraMapValue =
269 "extra value following label %s in extra info map";
270const char* const SExpParser::ErrorStrings::kUnexpectedComma =
271 "comma found outside extra info map";
272const char* const SExpParser::ErrorStrings::kUnexpectedRightParen =
273 "unexpected closing parenthesis";
274const char* const SExpParser::ErrorStrings::kUnexpectedRightCurly =
275 "unexpected closing curly brace";
276
277#define PARSE_ERROR(x, ...) \
278 StoreError(x, __VA_ARGS__); \
279 return nullptr
280
281SExpression* SExpParser::Parse() {
282 Reset();
283 while (auto token = GetNextToken()) {
284 const intptr_t start_pos = token->cstr() - buffer_;
285 switch (token->type()) {
286 case kLeftParen: {
287 if (in_extra_) {
288 if (cur_label_ == nullptr) {
289 PARSE_ERROR(start_pos, ErrorStrings::kNonSymbolLabel);
290 } else if (cur_value_ != nullptr) {
291 PARSE_ERROR(start_pos, ErrorStrings::kExtraMapValue, cur_label_);
292 }
293 }
294 auto sexp = new (zone_) SExpList(zone_, start_pos);
295 list_stack_.Add(sexp);
296 in_extra_stack_.Add(in_extra_);
297 extra_start_stack_.Add(extra_start_);
298 cur_label_stack_.Add(cur_label_);
299 in_extra_ = false;
300 extra_start_ = -1;
301 cur_label_ = nullptr;
302 break;
303 }
304 case kRightParen: {
305 if (list_stack_.is_empty()) {
306 PARSE_ERROR(start_pos, ErrorStrings::kUnexpectedRightParen);
307 }
308 if (in_extra_) {
309 PARSE_ERROR(start_pos, ErrorStrings::kOpenMap, extra_start_);
310 }
311 auto sexp = list_stack_.RemoveLast();
312 in_extra_ = in_extra_stack_.RemoveLast();
313 extra_start_ = extra_start_stack_.RemoveLast();
314 cur_label_ = cur_label_stack_.RemoveLast();
315 if (list_stack_.is_empty()) return sexp;
316 if (in_extra_) {
317 if (cur_label_ == nullptr) {
318 PARSE_ERROR(start_pos, ErrorStrings::kOpenMap, extra_start_);
319 }
320 cur_value_ = sexp;
321 } else {
322 list_stack_.Last()->Add(sexp);
323 }
324 break;
325 }
326 case kLeftCurly:
327 if (in_extra_) {
328 PARSE_ERROR(start_pos, ErrorStrings::kNestedMap);
329 }
330 if (list_stack_.is_empty()) {
331 PARSE_ERROR(start_pos, ErrorStrings::kMapOutsideList);
332 }
333 extra_start_ = start_pos;
334 in_extra_ = true;
335 break;
336 case kRightCurly:
337 if (!in_extra_ || list_stack_.is_empty()) {
338 PARSE_ERROR(start_pos, ErrorStrings::kUnexpectedRightCurly);
339 }
340 if (cur_label_ != nullptr) {
341 if (cur_value_ == nullptr) {
342 PARSE_ERROR(start_pos, ErrorStrings::kNoMapValue, cur_label_);
343 }
344 list_stack_.Last()->AddExtra(cur_label_, cur_value_);
345 cur_label_ = nullptr;
346 cur_value_ = nullptr;
347 }
348 in_extra_ = false;
349 extra_start_ = -1;
350 break;
351 case kComma: {
352 if (!in_extra_ || list_stack_.is_empty()) {
353 PARSE_ERROR(start_pos, ErrorStrings::kUnexpectedComma);
354 }
355 if (cur_label_ == nullptr) {
356 PARSE_ERROR(start_pos, ErrorStrings::kNoMapLabel);
357 } else if (cur_value_ == nullptr) {
358 PARSE_ERROR(start_pos, ErrorStrings::kNoMapValue, cur_label_);
359 }
360 list_stack_.Last()->AddExtra(cur_label_, cur_value_);
361 cur_label_ = nullptr;
362 cur_value_ = nullptr;
363 break;
364 }
365 case kSymbol: {
366 auto sexp = TokenToSExpression(token);
367 ASSERT(sexp->IsSymbol());
368 if (in_extra_) {
369 if (cur_value_ != nullptr) {
370 PARSE_ERROR(start_pos, ErrorStrings::kExtraMapValue, cur_label_);
371 }
372 if (cur_label_ == nullptr) {
373 const char* const label = sexp->AsSymbol()->value();
374 if (list_stack_.Last()->ExtraHasKey(label)) {
375 PARSE_ERROR(start_pos, ErrorStrings::kRepeatedMapLabel, label);
376 }
377 cur_label_ = sexp->AsSymbol()->value();
378 } else {
379 cur_value_ = sexp;
380 }
381 } else if (!list_stack_.is_empty()) {
382 list_stack_.Last()->Add(sexp);
383 } else {
384 return sexp;
385 }
386 break;
387 }
388 case kBoolean:
389 case kInteger:
390 case kDouble:
391 case kQuotedString: {
392 auto sexp = TokenToSExpression(token);
393 // TokenToSExpression has already set the error info, so just return.
394 if (sexp == nullptr) return nullptr;
395 if (in_extra_) {
396 if (cur_label_ == nullptr) {
397 PARSE_ERROR(start_pos, ErrorStrings::kNonSymbolLabel);
398 } else if (cur_value_ != nullptr) {
399 PARSE_ERROR(start_pos, ErrorStrings::kExtraMapValue, cur_label_);
400 }
401 cur_value_ = sexp;
402 } else if (!list_stack_.is_empty()) {
403 list_stack_.Last()->Add(sexp);
404 } else {
405 return sexp;
406 }
407 break;
408 }
409 default:
410 UNREACHABLE();
411 }
412 }
413 if (in_extra_) {
414 PARSE_ERROR(buffer_size_, ErrorStrings::kOpenMap, extra_start_);
415 } else if (!list_stack_.is_empty()) {
416 const intptr_t list_start = list_stack_.Last()->start();
417 PARSE_ERROR(buffer_size_, ErrorStrings::kOpenSExpList, list_start);
418 }
419 UNREACHABLE();
420}
421
422SExpression* SExpParser::TokenToSExpression(Token* token) {
423 const intptr_t start_pos = token->cstr() - buffer_;
424 switch (token->type()) {
425 case kSymbol:
426 return new (zone_) SExpSymbol(token->ToCString(zone_), start_pos);
427 case kInteger: {
428 const char* cstr = token->ToCString(zone_);
429 int64_t val;
430 if (!OS::StringToInt64(cstr, &val)) return nullptr;
431 return new (zone_) SExpInteger(val, start_pos);
432 }
433 case kBoolean: {
434 const bool is_true =
435 strncmp(token->cstr(), kBoolTrueSymbol, token->length()) == 0;
436 ASSERT(is_true ||
437 strncmp(token->cstr(), kBoolFalseSymbol, token->length()) == 0);
438 return new (zone_) SExpBool(is_true, start_pos);
439 }
440 case kDouble: {
441 double val;
442 if (!CStringToDouble(token->cstr(), token->length(), &val)) {
443 return nullptr;
444 }
445 return new (zone_) SExpDouble(val, start_pos);
446 }
447 case kQuotedString: {
448 const char* const cstr = token->cstr();
449 char* const buf = zone_->Alloc<char>(token->length());
450 // Skip the initial quote
451 ASSERT(cstr[0] == '"');
452 intptr_t old_pos = 1;
453 intptr_t new_pos = 0;
454 // The string _should_ end in a quote.
455 while (old_pos < token->length() - 1) {
456 if (cstr[old_pos] == '"') break;
457 if (cstr[old_pos] != '\\') {
458 buf[new_pos++] = cstr[old_pos++];
459 continue;
460 }
461 old_pos++;
462 if (old_pos >= token->length()) {
463 PARSE_ERROR(start_pos + old_pos, ErrorStrings::kOpenString,
464 start_pos);
465 }
466 const intptr_t escape_pos = start_pos + old_pos - 1;
467 switch (cstr[old_pos]) {
468 case 'b':
469 buf[new_pos] = '\b';
470 break;
471 case 'f':
472 buf[new_pos] = '\f';
473 break;
474 case 'n':
475 buf[new_pos] = '\n';
476 break;
477 case 'r':
478 buf[new_pos] = '\r';
479 break;
480 case 't':
481 buf[new_pos] = '\t';
482 break;
483 case 'u': {
484 const intptr_t first = old_pos + 1;
485 const intptr_t last = old_pos + 4;
486 if (last >= token->length()) {
487 PARSE_ERROR(escape_pos, ErrorStrings::kBadUnicodeEscape);
488 }
489 intptr_t val = 0;
490 for (const char *cursor = cstr + first, *end = cstr + last + 1;
491 cursor < end; cursor++) {
492 val *= 16;
493 if (!Utils::IsHexDigit(*cursor)) {
494 PARSE_ERROR(escape_pos, ErrorStrings::kBadUnicodeEscape);
495 }
496 val += Utils::HexDigitToInt(*cursor);
497 }
498 // Currently, just handle encoded ASCII instead of doing
499 // handling Unicode characters.
500 // (TextBuffer::AddEscapedString uses this for characters < 0x20.)
501 ASSERT(val <= 0x7F);
502 old_pos = last;
503 buf[new_pos] = val;
504 break;
505 }
506 default:
507 // Identity escapes.
508 buf[new_pos] = cstr[old_pos];
509 break;
510 }
511 old_pos++;
512 new_pos++;
513 }
514 if (cstr[old_pos] != '"') {
515 PARSE_ERROR(start_pos + token->length(), ErrorStrings::kOpenString,
516 start_pos);
517 }
518 buf[new_pos] = '\0';
519 return new (zone_) SExpString(buf, start_pos);
520 }
521 default:
522 UNREACHABLE();
523 }
524}
525
526#undef PARSE_ERROR
527
528SExpParser::Token* SExpParser::GetNextToken() {
529 intptr_t start_pos = cur_pos_;
530 while (start_pos < buffer_size_) {
531 if (isspace(buffer_[start_pos]) == 0) break;
532 start_pos++;
533 }
534 if (start_pos >= buffer_size_) return nullptr;
535 const char* start = buffer_ + start_pos;
536 switch (*start) {
537 case '(':
538 cur_pos_ = start_pos + 1;
539 return new (zone_) Token(kLeftParen, start, 1);
540 case ')':
541 cur_pos_ = start_pos + 1;
542 return new (zone_) Token(kRightParen, start, 1);
543 case ',':
544 cur_pos_ = start_pos + 1;
545 return new (zone_) Token(kComma, start, 1);
546 case '{':
547 cur_pos_ = start_pos + 1;
548 return new (zone_) Token(kLeftCurly, start, 1);
549 case '}':
550 cur_pos_ = start_pos + 1;
551 return new (zone_) Token(kRightCurly, start, 1);
552 case '"': {
553 intptr_t len = 1;
554 while (start_pos + len < buffer_size_) {
555 char curr = start[len];
556 len++; // Length should include the quote, if any.
557 if (curr == '\\') {
558 // Skip past next character (if any), since it cannot
559 // end the quoted string due to being escaped.
560 if (start_pos + len >= buffer_size_) break;
561 len++;
562 continue;
563 }
564 if (curr == '"') break;
565 }
566 cur_pos_ = start_pos + len;
567 return new (zone_) Token(kQuotedString, start, len);
568 }
569 default:
570 break;
571 }
572 intptr_t len = 0;
573 // Start number detection after possible negation sign.
574 if (start[len] == '-') {
575 len++;
576 if ((start_pos + len) >= buffer_size_) {
577 cur_pos_ = start_pos + len;
578 return new (zone_) Token(kSymbol, start, len);
579 }
580 }
581 // Keep the currently detected token type. Start off by assuming we have
582 // an integer, then fall back to doubles if we see parts appropriate for
583 // those but not integers, and fall back to symbols otherwise.
584 TokenType type = kInteger;
585 bool saw_exponent = false;
586 while ((start_pos + len) < buffer_size_) {
587 // Both numbers and symbols cannot contain these values, so we are at the
588 // end of whichever one we're in.
589 if (!IsSymbolContinue(start[len])) break;
590 if (type == kInteger && start[len] == '.') {
591 type = kDouble;
592 len++;
593 continue;
594 }
595 if (type != kSymbol && !saw_exponent && start[len] == kDoubleExponentChar) {
596 saw_exponent = true;
597 type = kDouble;
598 len++;
599 // Skip past negation in exponent if any.
600 if ((start_pos + len) < buffer_size_ && start[len] == '-') len++;
601 continue;
602 }
603 // If we find a character that can't appear in a number, then fall back
604 // to symbol-ness.
605 if (isdigit(start[len]) == 0) type = kSymbol;
606 len++;
607 }
608 cur_pos_ = start_pos + len;
609 // Skip special symbol detection if we don't have a symbol.
610 if (type != kSymbol) return new (zone_) Token(type, start, len);
611 // Check for special symbols used for booleans and certain Double values.
612 switch (len) {
613 case 3:
614 if (strncmp(start, kDoubleNaNSymbol, len) == 0) type = kDouble;
615 break;
616 case 4:
617 if (strncmp(start, kBoolTrueSymbol, len) == 0) type = kBoolean;
618 break;
619 case 5:
620 if (strncmp(start, kBoolFalseSymbol, len) == 0) type = kBoolean;
621 break;
622 case 8:
623 if (strncmp(start, kDoubleInfinitySymbol, len) == 0) type = kDouble;
624 break;
625 case 9:
626 if (start[0] == '-' &&
627 strncmp(start + 1, kDoubleInfinitySymbol, len - 1) == 0) {
628 type = kDouble;
629 }
630 break;
631 default:
632 break;
633 }
634 return new (zone_) Token(type, start, len);
635}
636
637bool SExpParser::IsSymbolContinue(char c) {
638 return (isspace(c) == 0) && c != '(' && c != ')' && c != ',' && c != '{' &&
639 c != '}' && c != '"';
640}
641
642const char* const SExpParser::Token::TokenNames[kMaxTokens] = {
643#define S_EXP_TOKEN_NAME_STRING(name) #name,
644 S_EXP_TOKEN_LIST(S_EXP_TOKEN_NAME_STRING)
645#undef S_EXP_TOKEN_NAME_STRING
646};
647
648const char* SExpParser::Token::ToCString(Zone* zone) {
649 char* const buffer = zone->Alloc<char>(len_ + 1);
650 strncpy(buffer, cstr_, len_);
651 buffer[len_] = '\0';
652 return buffer;
653}
654
655void SExpParser::Reset() {
656 cur_pos_ = 0;
657 in_extra_ = false;
658 extra_start_ = -1;
659 cur_label_ = nullptr;
660 cur_value_ = nullptr;
661 list_stack_.Clear();
662 in_extra_stack_.Clear();
663 extra_start_stack_.Clear();
664 cur_label_stack_.Clear();
665 error_pos_ = -1;
666 error_message_ = nullptr;
667}
668
669void SExpParser::StoreError(intptr_t pos, const char* format, ...) {
670 va_list args;
671 va_start(args, format);
672 const char* const message = OS::VSCreate(zone_, format, args);
673 va_end(args);
674 error_pos_ = pos;
675 error_message_ = message;
676}
677
678void SExpParser::ReportError() const {
679 ASSERT(error_message_ != nullptr);
680 ASSERT(error_pos_ >= 0);
681 OS::PrintErr("Unable to parse s-expression: %s\n", buffer_);
682 OS::PrintErr("Error at character %" Pd ": %s\n", error_pos_, error_message_);
683 OS::Abort();
684}
685
686} // namespace dart
687