]> git.saurik.com Git - cycript.git/blame_incremental - Output.cpp
Correctly handle "high bytes" in "native" strings.
[cycript.git] / Output.cpp
... / ...
CommitLineData
1/* Cycript - The Truly Universal Scripting Language
2 * Copyright (C) 2009-2016 Jay Freeman (saurik)
3*/
4
5/* GNU Affero General Public License, Version 3 {{{ */
6/*
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19**/
20/* }}} */
21
22#include <cmath>
23#include <iomanip>
24#include <sstream>
25
26#include "Syntax.hpp"
27
28enum CYStringType {
29 CYStringTypeSingle,
30 CYStringTypeDouble,
31 CYStringTypeTemplate,
32};
33
34void CYStringify(std::ostringstream &str, const char *data, size_t size, CYStringifyMode mode) {
35 if (size == 0) {
36 str << "\"\"";
37 return;
38 }
39
40 unsigned quot(0), apos(0), tick(0), line(0);
41 for (const char *value(data), *end(data + size); value != end; ++value)
42 switch (*value) {
43 case '"': ++quot; break;
44 case '\'': ++apos; break;
45 case '`': ++tick; break;
46 case '$': ++tick; break;
47 case '\n': ++line; break;
48 }
49
50 bool split;
51 if (mode != CYStringifyModeCycript)
52 split = false;
53 else {
54 double ratio(double(line) / size);
55 split = size > 10 && line > 2 && ratio > 0.005 && ratio < 0.10;
56 }
57
58 CYStringType type;
59 if (mode == CYStringifyModeNative)
60 type = CYStringTypeDouble;
61 else if (split)
62 type = CYStringTypeTemplate;
63 else if (quot > apos)
64 type = CYStringTypeSingle;
65 else
66 type = CYStringTypeDouble;
67
68 bool parens(split && mode != CYStringifyModeNative && type != CYStringTypeTemplate);
69 if (parens)
70 str << '(';
71
72 char border;
73 switch (type) {
74 case CYStringTypeSingle: border = '\''; break;
75 case CYStringTypeDouble: border = '"'; break;
76 case CYStringTypeTemplate: border = '`'; break;
77 }
78
79 str << border;
80
81 bool space(false);
82
83 for (const char *value(data), *end(data + size); value != end; ++value)
84 if (*value == ' ') {
85 space = true;
86 str << ' ';
87 } else { switch (uint8_t next = *value) {
88 case '\\': str << "\\\\"; break;
89 case '\b': str << "\\b"; break;
90 case '\f': str << "\\f"; break;
91 case '\r': str << "\\r"; break;
92 case '\t': str << "\\t"; break;
93 case '\v': str << "\\v"; break;
94
95 case '\a':
96 if (mode == CYStringifyModeNative)
97 str << "\\a";
98 else goto simple;
99 break;
100
101 case '\n':
102 if (!split)
103 str << "\\n";
104 /*else if (mode == CYStringifyModeNative)
105 str << border << "\\\n" << border;*/
106 else if (type != CYStringTypeTemplate)
107 str << border << '+' << border;
108 else if (!space)
109 str << '\n';
110 else
111 str << "\\n\\\n";
112 break;
113
114 case '$':
115 if (type == CYStringTypeTemplate)
116 str << "\\$";
117 else goto simple;
118 break;
119
120 case '`':
121 if (type == CYStringTypeTemplate)
122 str << "\\`";
123 else goto simple;
124 break;
125
126 case '"':
127 if (type == CYStringTypeDouble)
128 str << "\\\"";
129 else goto simple;
130 break;
131
132 case '\'':
133 if (type == CYStringTypeSingle)
134 str << "\\'";
135 else goto simple;
136 break;
137
138 case '\0':
139 if (mode != CYStringifyModeNative && value[1] >= '0' && value[1] <= '9')
140 str << "\\x00";
141 else
142 str << "\\0";
143 break;
144
145 default:
146 if (next >= 0x20 && next < 0x7f) simple:
147 str << *value;
148 else if (mode == CYStringifyModeNative)
149 str << "\\x" << std::setbase(16) << std::setw(2) << std::setfill('0') << unsigned(*value & 0xff);
150 else {
151 unsigned levels(1);
152 if ((next & 0x80) != 0)
153 while ((next & 0x80 >> ++levels) != 0);
154
155 unsigned point(next & 0xff >> levels);
156 while (--levels != 0)
157 point = point << 6 | uint8_t(*++value) & 0x3f;
158
159 if (point < 0x100)
160 str << "\\x" << std::setbase(16) << std::setw(2) << std::setfill('0') << point;
161 else if (point < 0x10000)
162 str << "\\u" << std::setbase(16) << std::setw(4) << std::setfill('0') << point;
163 else {
164 point -= 0x10000;
165 str << "\\u" << std::setbase(16) << std::setw(4) << std::setfill('0') << (0xd800 | point >> 0x0a);
166 str << "\\u" << std::setbase(16) << std::setw(4) << std::setfill('0') << (0xdc00 | point & 0x3ff);
167 }
168 }
169 } space = false; }
170
171 str << border;
172
173 if (parens)
174 str << ')';
175}
176
177void CYNumerify(std::ostringstream &str, double value) {
178 if (std::isinf(value)) {
179 if (value < 0)
180 str << '-';
181 str << "Infinity";
182 return;
183 }
184
185 char string[32];
186 // XXX: I want this to print 1e3 rather than 1000
187 sprintf(string, "%.17g", value);
188 str << string;
189}
190
191void CYOutput::Terminate() {
192 operator ()(';');
193 mode_ = NoMode;
194}
195
196CYOutput &CYOutput::operator <<(char rhs) {
197 if (rhs == ' ' || rhs == '\n')
198 if (pretty_)
199 operator ()(rhs);
200 else goto done;
201 else if (rhs == '\t')
202 if (pretty_)
203 for (unsigned i(0); i != indent_; ++i)
204 operator ()(" ", 4);
205 else goto done;
206 else if (rhs == '\r') {
207 if (right_) {
208 operator ()('\n');
209 right_ = false;
210 } goto done;
211 } else goto work;
212
213 right_ = true;
214 mode_ = NoMode;
215 goto done;
216
217 work:
218 if (mode_ == Terminated && rhs != '}') {
219 right_ = true;
220 operator ()(';');
221 }
222
223 if (rhs == ';') {
224 if (pretty_)
225 goto none;
226 else {
227 mode_ = Terminated;
228 goto done;
229 }
230 } else if (rhs == '+') {
231 if (mode_ == NoPlus)
232 operator ()(' ');
233 mode_ = NoPlus;
234 } else if (rhs == '-') {
235 if (mode_ == NoHyphen)
236 operator ()(' ');
237 mode_ = NoHyphen;
238 } else if (WordEndRange_[rhs]) {
239 if (mode_ == NoLetter)
240 operator ()(' ');
241 mode_ = NoLetter;
242 } else none:
243 mode_ = NoMode;
244
245 right_ = true;
246 operator ()(rhs);
247 done:
248 return *this;
249}
250
251CYOutput &CYOutput::operator <<(const char *rhs) {
252 size_t size(strlen(rhs));
253
254 if (size == 1)
255 return *this << *rhs;
256
257 if (mode_ == Terminated)
258 operator ()(';');
259 else if (
260 mode_ == NoPlus && *rhs == '+' ||
261 mode_ == NoHyphen && *rhs == '-' ||
262 mode_ == NoLetter && WordEndRange_[*rhs]
263 )
264 operator ()(' ');
265
266 char last(rhs[size - 1]);
267 if (WordEndRange_[last] || last == '/')
268 mode_ = NoLetter;
269 else
270 mode_ = NoMode;
271
272 right_ = true;
273 operator ()(rhs, size);
274 return *this;
275}
276
277void CYArgument::Output(CYOutput &out) const {
278 if (name_ != NULL) {
279 out << *name_;
280 if (value_ != NULL)
281 out << ':' << ' ';
282 }
283 if (value_ != NULL)
284 value_->Output(out, CYAssign::Precedence_, CYNoFlags);
285 if (next_ != NULL) {
286 out << ',';
287 out << ' ' << *next_;
288 }
289}
290
291void CYArray::Output(CYOutput &out, CYFlags flags) const {
292 out << '[' << elements_ << ']';
293}
294
295void CYArrayComprehension::Output(CYOutput &out, CYFlags flags) const {
296 out << '[' << *expression_ << ' ' << *comprehensions_ << ']';
297}
298
299void CYAssignment::Output(CYOutput &out, CYFlags flags) const {
300 lhs_->Output(out, Precedence() - 1, CYLeft(flags) | CYNoRightHand);
301 out << ' ' << Operator() << ' ';
302 rhs_->Output(out, Precedence(), CYRight(flags));
303}
304
305void CYBlock::Output(CYOutput &out, CYFlags flags) const {
306 out << '{' << '\n';
307 ++out.indent_;
308 out << code_;
309 --out.indent_;
310 out << '\t' << '}';
311}
312
313void CYBoolean::Output(CYOutput &out, CYFlags flags) const {
314 out << '!' << (Value() ? "0" : "1");
315 if ((flags & CYNoInteger) != 0)
316 out << '.';
317}
318
319void CYBreak::Output(CYOutput &out, CYFlags flags) const {
320 out << "break";
321 if (label_ != NULL)
322 out << ' ' << *label_;
323 out << ';';
324}
325
326void CYCall::Output(CYOutput &out, CYFlags flags) const {
327 bool protect((flags & CYNoCall) != 0);
328 if (protect)
329 out << '(';
330 function_->Output(out, Precedence(), protect ? CYNoFlags : flags);
331 out << '(' << arguments_ << ')';
332 if (protect)
333 out << ')';
334}
335
336namespace cy {
337namespace Syntax {
338
339void Catch::Output(CYOutput &out) const {
340 out << ' ' << "catch" << ' ' << '(' << *name_ << ')' << ' ';
341 out << '{' << '\n';
342 ++out.indent_;
343 out << code_;
344 --out.indent_;
345 out << '\t' << '}';
346}
347
348} }
349
350void CYClassExpression::Output(CYOutput &out, CYFlags flags) const {
351 bool protect((flags & CYNoClass) != 0);
352 if (protect)
353 out << '(';
354 out << "class";
355 if (name_ != NULL)
356 out << ' ' << *name_;
357 out << *tail_;;
358 if (protect)
359 out << ')';
360}
361
362void CYClassStatement::Output(CYOutput &out, CYFlags flags) const {
363 out << "class" << ' ' << *name_ << *tail_;
364}
365
366void CYClassTail::Output(CYOutput &out) const {
367 if (extends_ == NULL)
368 out << ' ';
369 else {
370 out << '\n';
371 ++out.indent_;
372 out << "extends" << ' ';
373 extends_->Output(out, CYAssign::Precedence_ - 1, CYNoFlags);
374 out << '\n';
375 --out.indent_;
376 }
377
378 out << '{' << '\n';
379 ++out.indent_;
380
381 --out.indent_;
382 out << '}';
383}
384
385void CYCompound::Output(CYOutput &out, CYFlags flags) const {
386 if (next_ == NULL)
387 expression_->Output(out, flags);
388 else {
389 expression_->Output(out, CYLeft(flags));
390 out << ',' << ' ';
391 next_->Output(out, CYRight(flags));
392 }
393}
394
395void CYComputed::PropertyName(CYOutput &out) const {
396 out << '[';
397 expression_->Output(out, CYAssign::Precedence_, CYNoFlags);
398 out << ']';
399}
400
401void CYCondition::Output(CYOutput &out, CYFlags flags) const {
402 test_->Output(out, Precedence() - 1, CYLeft(flags));
403 out << ' ' << '?' << ' ';
404 if (true_ != NULL)
405 true_->Output(out, CYAssign::Precedence_, CYNoColon);
406 out << ' ' << ':' << ' ';
407 false_->Output(out, CYAssign::Precedence_, CYRight(flags));
408}
409
410void CYContinue::Output(CYOutput &out, CYFlags flags) const {
411 out << "continue";
412 if (label_ != NULL)
413 out << ' ' << *label_;
414 out << ';';
415}
416
417void CYClause::Output(CYOutput &out) const {
418 out << '\t';
419 if (value_ == NULL)
420 out << "default";
421 else {
422 out << "case" << ' ';
423 value_->Output(out, CYNoColon);
424 }
425 out << ':' << '\n';
426 ++out.indent_;
427 out << code_;
428 --out.indent_;
429 out << next_;
430}
431
432void CYDebugger::Output(CYOutput &out, CYFlags flags) const {
433 out << "debugger" << ';';
434}
435
436void CYBinding::Output(CYOutput &out, CYFlags flags) const {
437 out << *identifier_;
438 //out.out_ << ':' << identifier_->usage_ << '#' << identifier_->offset_;
439 if (initializer_ != NULL) {
440 out << ' ' << '=' << ' ';
441 initializer_->Output(out, CYAssign::Precedence_, CYRight(flags));
442 }
443}
444
445void CYBindings::Output(CYOutput &out) const {
446 Output(out, CYNoFlags);
447}
448
449void CYBindings::Output(CYOutput &out, CYFlags flags) const {
450 const CYBindings *binding(this);
451 bool first(true);
452
453 for (;;) {
454 CYBindings *next(binding->next_);
455
456 CYFlags jacks(first ? CYLeft(flags) : next == NULL ? CYRight(flags) : CYCenter(flags));
457 first = false;
458 binding->binding_->Output(out, jacks);
459
460 if (next == NULL)
461 break;
462
463 out << ',' << ' ';
464 binding = next;
465 }
466}
467
468void CYDirectMember::Output(CYOutput &out, CYFlags flags) const {
469 object_->Output(out, Precedence(), CYLeft(flags) | CYNoInteger);
470 if (const char *word = property_->Word())
471 out << '.' << word;
472 else
473 out << '[' << *property_ << ']';
474}
475
476void CYDoWhile::Output(CYOutput &out, CYFlags flags) const {
477 out << "do";
478
479 unsigned line(out.position_.line);
480 unsigned indent(out.indent_);
481 code_->Single(out, CYCenter(flags), CYCompactLong);
482
483 if (out.position_.line != line && out.recent_ == indent)
484 out << ' ';
485 else
486 out << '\n' << '\t';
487
488 out << "while" << ' ' << '(' << *test_ << ')';
489}
490
491void CYElementSpread::Output(CYOutput &out) const {
492 out << "..." << value_;
493}
494
495void CYElementValue::Output(CYOutput &out) const {
496 if (value_ != NULL)
497 value_->Output(out, CYAssign::Precedence_, CYNoFlags);
498 if (next_ != NULL || value_ == NULL) {
499 out << ',';
500 if (next_ != NULL && !next_->Elision())
501 out << ' ';
502 }
503 if (next_ != NULL)
504 next_->Output(out);
505}
506
507void CYEmpty::Output(CYOutput &out, CYFlags flags) const {
508 out.Terminate();
509}
510
511void CYEval::Output(CYOutput &out, CYFlags flags) const {
512 _assert(false);
513}
514
515void CYExpress::Output(CYOutput &out, CYFlags flags) const {
516 expression_->Output(out, flags | CYNoBFC);
517 out << ';';
518}
519
520void CYExpression::Output(CYOutput &out) const {
521 Output(out, CYNoFlags);
522}
523
524void CYExpression::Output(CYOutput &out, int precedence, CYFlags flags) const {
525 if (precedence < Precedence() || (flags & CYNoRightHand) != 0 && RightHand())
526 out << '(' << *this << ')';
527 else
528 Output(out, flags);
529}
530
531void CYExtend::Output(CYOutput &out, CYFlags flags) const {
532 lhs_->Output(out, CYLeft(flags));
533 out << ' ' << object_;
534}
535
536void CYExternalDefinition::Output(CYOutput &out, CYFlags flags) const {
537 out << "extern" << ' ' << abi_ << ' ';
538 type_->Output(out, name_);
539 out.Terminate();
540}
541
542void CYExternalExpression::Output(CYOutput &out, CYFlags flags) const {
543 out << '(' << "extern" << ' ' << abi_ << ' ';
544 type_->Output(out, name_);
545 out << ')';
546}
547
548void CYFatArrow::Output(CYOutput &out, CYFlags flags) const {
549 out << '(' << parameters_ << ')' << ' ' << "=>" << ' ' << '{' << code_ << '}';
550}
551
552void CYFinally::Output(CYOutput &out) const {
553 out << ' ' << "finally" << ' ';
554 out << '{' << '\n';
555 ++out.indent_;
556 out << code_;
557 --out.indent_;
558 out << '\t' << '}';
559}
560
561void CYFor::Output(CYOutput &out, CYFlags flags) const {
562 out << "for" << ' ' << '(';
563 if (initializer_ != NULL)
564 initializer_->Output(out, CYNoIn);
565 out.Terminate();
566 if (test_ != NULL)
567 out << ' ';
568 out << test_;
569 out.Terminate();
570 if (increment_ != NULL)
571 out << ' ';
572 out << increment_;
573 out << ')';
574 code_->Single(out, CYRight(flags), CYCompactShort);
575}
576
577void CYForLexical::Output(CYOutput &out, CYFlags flags) const {
578 out << (constant_ ? "const" : "let") << ' ';
579 binding_->Output(out, CYRight(flags));
580}
581
582void CYForIn::Output(CYOutput &out, CYFlags flags) const {
583 out << "for" << ' ' << '(';
584 initializer_->Output(out, CYNoIn | CYNoRightHand);
585 out << ' ' << "in" << ' ' << *iterable_ << ')';
586 code_->Single(out, CYRight(flags), CYCompactShort);
587}
588
589void CYForInitialized::Output(CYOutput &out, CYFlags flags) const {
590 out << "for" << ' ' << '(' << "var" << ' ';
591 binding_->Output(out, CYNoIn | CYNoRightHand);
592 out << ' ' << "in" << ' ' << *iterable_ << ')';
593 code_->Single(out, CYRight(flags), CYCompactShort);
594}
595
596void CYForInComprehension::Output(CYOutput &out) const {
597 out << "for" << ' ' << '(';
598 binding_->Output(out, CYNoIn | CYNoRightHand);
599 out << ' ' << "in" << ' ' << *iterable_ << ')';
600}
601
602void CYForOf::Output(CYOutput &out, CYFlags flags) const {
603 out << "for" << ' ' << '(';
604 initializer_->Output(out, CYNoRightHand);
605 out << ' ' << "of" << ' ' << *iterable_ << ')';
606 code_->Single(out, CYRight(flags), CYCompactShort);
607}
608
609void CYForOfComprehension::Output(CYOutput &out) const {
610 out << "for" << ' ' << '(';
611 binding_->Output(out, CYNoRightHand);
612 out << ' ' << "of" << ' ' << *iterable_ << ')' << next_;
613}
614
615void CYForVariable::Output(CYOutput &out, CYFlags flags) const {
616 out << "var" << ' ';
617 binding_->Output(out, CYRight(flags));
618}
619
620void CYFunction::Output(CYOutput &out) const {
621 out << '(' << parameters_ << ')' << ' ';
622 out << '{' << '\n';
623 ++out.indent_;
624 out << code_;
625 --out.indent_;
626 out << '\t' << '}';
627}
628
629void CYFunctionExpression::Output(CYOutput &out, CYFlags flags) const {
630 // XXX: one could imagine using + here to save a byte
631 bool protect((flags & CYNoFunction) != 0);
632 if (protect)
633 out << '(';
634 out << "function";
635 if (name_ != NULL)
636 out << ' ' << *name_;
637 CYFunction::Output(out);
638 if (protect)
639 out << ')';
640}
641
642void CYFunctionStatement::Output(CYOutput &out, CYFlags flags) const {
643 out << "function" << ' ' << *name_;
644 CYFunction::Output(out);
645}
646
647void CYFunctionParameter::Output(CYOutput &out) const {
648 binding_->Output(out, CYNoFlags);
649 if (next_ != NULL)
650 out << ',' << ' ' << *next_;
651}
652
653const char *CYIdentifier::Word() const {
654 return next_ == NULL || next_ == this ? CYWord::Word() : next_->Word();
655}
656
657void CYIf::Output(CYOutput &out, CYFlags flags) const {
658 bool protect(false);
659 if (false_ == NULL && (flags & CYNoDangle) != 0) {
660 protect = true;
661 out << '{';
662 }
663
664 out << "if" << ' ' << '(' << *test_ << ')';
665
666 CYFlags right(protect ? CYNoFlags : CYRight(flags));
667
668 CYFlags jacks(CYNoDangle);
669 if (false_ == NULL)
670 jacks |= right;
671 else
672 jacks |= protect ? CYNoFlags : CYCenter(flags);
673
674 unsigned line(out.position_.line);
675 unsigned indent(out.indent_);
676 true_->Single(out, jacks, CYCompactShort);
677
678 if (false_ != NULL) {
679 if (out.position_.line != line && out.recent_ == indent)
680 out << ' ';
681 else
682 out << '\n' << '\t';
683
684 out << "else";
685 false_->Single(out, right, CYCompactLong);
686 }
687
688 if (protect)
689 out << '}';
690}
691
692void CYIfComprehension::Output(CYOutput &out) const {
693 out << "if" << ' ' << '(' << *test_ << ')' << next_;
694}
695
696void CYImport::Output(CYOutput &out, CYFlags flags) const {
697 out << "@import";
698}
699
700void CYImportDeclaration::Output(CYOutput &out, CYFlags flags) const {
701 _assert(false);
702}
703
704void CYIndirect::Output(CYOutput &out, CYFlags flags) const {
705 out << "*";
706 rhs_->Output(out, Precedence(), CYRight(flags));
707}
708
709void CYIndirectMember::Output(CYOutput &out, CYFlags flags) const {
710 object_->Output(out, Precedence(), CYLeft(flags));
711 if (const char *word = property_->Word())
712 out << "->" << word;
713 else
714 out << "->" << '[' << *property_ << ']';
715}
716
717void CYInfix::Output(CYOutput &out, CYFlags flags) const {
718 const char *name(Operator());
719 bool protect((flags & CYNoIn) != 0 && strcmp(name, "in") == 0);
720 if (protect)
721 out << '(';
722 CYFlags left(protect ? CYNoFlags : CYLeft(flags));
723 lhs_->Output(out, Precedence(), left);
724 out << ' ' << name << ' ';
725 CYFlags right(protect ? CYNoFlags : CYRight(flags));
726 rhs_->Output(out, Precedence() - 1, right);
727 if (protect)
728 out << ')';
729}
730
731void CYLabel::Output(CYOutput &out, CYFlags flags) const {
732 out << *name_ << ':';
733 statement_->Single(out, CYRight(flags), CYCompactShort);
734}
735
736void CYParenthetical::Output(CYOutput &out, CYFlags flags) const {
737 out << '(';
738 expression_->Output(out, CYCompound::Precedence_, CYNoFlags);
739 out << ')';
740}
741
742void CYStatement::Output(CYOutput &out) const {
743 Multiple(out);
744}
745
746void CYTemplate::Output(CYOutput &out, CYFlags flags) const {
747 _assert(false);
748}
749
750void CYTypeArrayOf::Output(CYOutput &out, CYPropertyName *name) const {
751 next_->Output(out, Precedence(), name, false);
752 out << '[';
753 out << size_;
754 out << ']';
755}
756
757void CYTypeBlockWith::Output(CYOutput &out, CYPropertyName *name) const {
758 out << '(' << '^';
759 next_->Output(out, Precedence(), name, false);
760 out << ')' << '(' << parameters_ << ')';
761}
762
763void CYTypeConstant::Output(CYOutput &out, CYPropertyName *name) const {
764 out << "const";
765 next_->Output(out, Precedence(), name, false);
766}
767
768void CYTypeFunctionWith::Output(CYOutput &out, CYPropertyName *name) const {
769 next_->Output(out, Precedence(), name, false);
770 out << '(' << parameters_;
771 if (variadic_) {
772 if (parameters_ != NULL)
773 out << ',' << ' ';
774 out << "...";
775 }
776 out << ')';
777}
778
779void CYTypePointerTo::Output(CYOutput &out, CYPropertyName *name) const {
780 out << '*';
781 next_->Output(out, Precedence(), name, false);
782}
783
784void CYTypeVolatile::Output(CYOutput &out, CYPropertyName *name) const {
785 out << "volatile";
786 next_->Output(out, Precedence(), name, true);
787}
788
789void CYTypeModifier::Output(CYOutput &out, int precedence, CYPropertyName *name, bool space) const {
790 if (this == NULL && name == NULL)
791 return;
792 else if (space)
793 out << ' ';
794
795 if (this == NULL) {
796 name->PropertyName(out);
797 return;
798 }
799
800 bool protect(precedence > Precedence());
801
802 if (protect)
803 out << '(';
804 Output(out, name);
805 if (protect)
806 out << ')';
807}
808
809void CYType::Output(CYOutput &out, CYPropertyName *name) const {
810 out << *specifier_;
811 modifier_->Output(out, 0, name, true);
812}
813
814void CYType::Output(CYOutput &out) const {
815 Output(out, NULL);
816}
817
818void CYEncodedType::Output(CYOutput &out, CYFlags flags) const {
819 out << "@encode(" << typed_ << ")";
820}
821
822void CYTypedParameter::Output(CYOutput &out) const {
823 type_->Output(out, name_);
824 if (next_ != NULL)
825 out << ',' << ' ' << next_;
826}
827
828void CYLambda::Output(CYOutput &out, CYFlags flags) const {
829 // XXX: this is seriously wrong
830 out << "[](";
831 out << ")->";
832 out << "{";
833 out << "}";
834}
835
836void CYTypeDefinition::Output(CYOutput &out, CYFlags flags) const {
837 out << "typedef" << ' ';
838 type_->Output(out, name_);
839 out.Terminate();
840}
841
842void CYTypeExpression::Output(CYOutput &out, CYFlags flags) const {
843 out << '(' << "typedef" << ' ' << *typed_ << ')';
844}
845
846void CYLexical::Output(CYOutput &out, CYFlags flags) const {
847 out << "let" << ' ';
848 bindings_->Output(out, flags); // XXX: flags
849 out << ';';
850}
851
852void CYModule::Output(CYOutput &out) const {
853 out << part_;
854 if (next_ != NULL)
855 out << '.' << next_;
856}
857
858namespace cy {
859namespace Syntax {
860
861void New::Output(CYOutput &out, CYFlags flags) const {
862 out << "new" << ' ';
863 CYFlags jacks(CYNoCall | CYCenter(flags));
864 constructor_->Output(out, Precedence(), jacks);
865 if (arguments_ != NULL)
866 out << '(' << *arguments_ << ')';
867}
868
869} }
870
871void CYNull::Output(CYOutput &out, CYFlags flags) const {
872 out << "null";
873}
874
875void CYNumber::Output(CYOutput &out, CYFlags flags) const {
876 std::ostringstream str;
877 CYNumerify(str, Value());
878 std::string value(str.str());
879 out << value.c_str();
880 // XXX: this should probably also handle hex conversions and exponents
881 if ((flags & CYNoInteger) != 0 && value.find('.') == std::string::npos)
882 out << '.';
883}
884
885void CYNumber::PropertyName(CYOutput &out) const {
886 Output(out, CYNoFlags);
887}
888
889void CYObject::Output(CYOutput &out, CYFlags flags) const {
890 bool protect((flags & CYNoBrace) != 0);
891 if (protect)
892 out << '(';
893 out << '{' << '\n';
894 ++out.indent_;
895 out << properties_;
896 --out.indent_;
897 out << '\t' << '}';
898 if (protect)
899 out << ')';
900}
901
902void CYPostfix::Output(CYOutput &out, CYFlags flags) const {
903 lhs_->Output(out, Precedence(), CYLeft(flags));
904 out << Operator();
905}
906
907void CYPrefix::Output(CYOutput &out, CYFlags flags) const {
908 const char *name(Operator());
909 out << name;
910 if (Alphabetic())
911 out << ' ';
912 rhs_->Output(out, Precedence(), CYRight(flags));
913}
914
915void CYScript::Output(CYOutput &out) const {
916 out << code_;
917}
918
919void CYProperty::Output(CYOutput &out) const {
920 if (next_ != NULL || out.pretty_)
921 out << ',';
922 out << '\n' << next_;
923}
924
925void CYPropertyGetter::Output(CYOutput &out) const {
926 out << "get" << ' ';
927 name_->PropertyName(out);
928 CYFunction::Output(out);
929 CYProperty::Output(out);
930}
931
932void CYPropertyMethod::Output(CYOutput &out) const {
933 name_->PropertyName(out);
934 CYFunction::Output(out);
935 CYProperty::Output(out);
936}
937
938void CYPropertySetter::Output(CYOutput &out) const {
939 out << "set" << ' ';
940 name_->PropertyName(out);
941 CYFunction::Output(out);
942 CYProperty::Output(out);
943}
944
945void CYPropertyValue::Output(CYOutput &out) const {
946 out << '\t';
947 name_->PropertyName(out);
948 out << ':' << ' ';
949 value_->Output(out, CYAssign::Precedence_, CYNoFlags);
950 CYProperty::Output(out);
951}
952
953void CYRegEx::Output(CYOutput &out, CYFlags flags) const {
954 out << Value();
955}
956
957void CYResolveMember::Output(CYOutput &out, CYFlags flags) const {
958 object_->Output(out, Precedence(), CYLeft(flags));
959 if (const char *word = property_->Word())
960 out << "::" << word;
961 else
962 out << "::" << '[' << *property_ << ']';
963}
964
965void CYReturn::Output(CYOutput &out, CYFlags flags) const {
966 out << "return";
967 if (value_ != NULL)
968 out << ' ' << *value_;
969 out << ';';
970}
971
972void CYRubyBlock::Output(CYOutput &out, CYFlags flags) const {
973 lhs_->Output(out, CYLeft(flags));
974 out << ' ';
975 proc_->Output(out, CYRight(flags));
976}
977
978void CYRubyProc::Output(CYOutput &out, CYFlags flags) const {
979 out << '{' << ' ' << '|' << parameters_ << '|' << '\n';
980 ++out.indent_;
981 out << code_;
982 --out.indent_;
983 out << '\t' << '}';
984}
985
986void CYSubscriptMember::Output(CYOutput &out, CYFlags flags) const {
987 object_->Output(out, Precedence(), CYLeft(flags));
988 out << "." << '[' << *property_ << ']';
989}
990
991void CYStatement::Multiple(CYOutput &out, CYFlags flags) const {
992 bool first(true);
993 CYForEach (next, this) {
994 bool last(next->next_ == NULL);
995 CYFlags jacks(first ? last ? flags : CYLeft(flags) : last ? CYRight(flags) : CYCenter(flags));
996 first = false;
997 out << '\t';
998 next->Output(out, jacks);
999 out << '\n';
1000 }
1001}
1002
1003void CYStatement::Single(CYOutput &out, CYFlags flags, CYCompactType request) const {
1004 if (this == NULL)
1005 return out.Terminate();
1006
1007 _assert(next_ == NULL);
1008
1009 CYCompactType compact(Compact());
1010
1011 if (compact >= request)
1012 out << ' ';
1013 else {
1014 out << '\n';
1015 ++out.indent_;
1016 out << '\t';
1017 }
1018
1019 Output(out, flags);
1020
1021 if (compact < request)
1022 --out.indent_;
1023}
1024
1025void CYString::Output(CYOutput &out, CYFlags flags) const {
1026 std::ostringstream str;
1027 CYStringify(str, value_, size_, CYStringifyModeLegacy);
1028 out << str.str().c_str();
1029}
1030
1031void CYString::PropertyName(CYOutput &out) const {
1032 if (const char *word = Word())
1033 out << word;
1034 else
1035 out << *this;
1036}
1037
1038static const char *Reserved_[] = {
1039 "false", "null", "true",
1040
1041 "break", "case", "catch", "continue", "default",
1042 "delete", "do", "else", "finally", "for", "function",
1043 "if", "in", "instanceof", "new", "return", "switch",
1044 "this", "throw", "try", "typeof", "var", "void",
1045 "while", "with",
1046
1047 "debugger", "const",
1048
1049 "class", "enum", "export", "extends", "import", "super",
1050
1051 "abstract", "boolean", "byte", "char", "double", "final",
1052 "float", "goto", "int", "long", "native", "short",
1053 "synchronized", "throws", "transient", "volatile",
1054
1055 "let", "yield",
1056
1057 NULL
1058};
1059
1060const char *CYString::Word() const {
1061 if (size_ == 0 || !WordStartRange_[value_[0]])
1062 return NULL;
1063 for (size_t i(1); i != size_; ++i)
1064 if (!WordEndRange_[value_[i]])
1065 return NULL;
1066 const char *value(Value());
1067 for (const char **reserved(Reserved_); *reserved != NULL; ++reserved)
1068 if (strcmp(*reserved, value) == 0)
1069 return NULL;
1070 return value;
1071}
1072
1073void CYStructDefinition::Output(CYOutput &out, CYFlags flags) const {
1074 out << "struct" << ' ' << *name_ << *tail_;
1075}
1076
1077void CYStructTail::Output(CYOutput &out) const {
1078 out << ' ' << '{' << '\n';
1079 ++out.indent_;
1080 CYForEach (field, fields_) {
1081 out << '\t';
1082 field->type_->Output(out, field->name_);
1083 out.Terminate();
1084 out << '\n';
1085 }
1086 --out.indent_;
1087 out << '\t' << '}';
1088}
1089
1090void CYSuperAccess::Output(CYOutput &out, CYFlags flags) const {
1091 out << "super";
1092 if (const char *word = property_->Word())
1093 out << '.' << word;
1094 else
1095 out << '[' << *property_ << ']';
1096}
1097
1098void CYSuperCall::Output(CYOutput &out, CYFlags flags) const {
1099 out << "super" << '(' << arguments_ << ')';
1100}
1101
1102void CYSwitch::Output(CYOutput &out, CYFlags flags) const {
1103 out << "switch" << ' ' << '(' << *value_ << ')' << ' ' << '{' << '\n';
1104 ++out.indent_;
1105 out << clauses_;
1106 --out.indent_;
1107 out << '\t' << '}';
1108}
1109
1110void CYSymbol::Output(CYOutput &out, CYFlags flags) const {
1111 bool protect((flags & CYNoColon) != 0);
1112 if (protect)
1113 out << '(';
1114 out << ':' << name_;
1115 if (protect)
1116 out << ')';
1117}
1118
1119void CYThis::Output(CYOutput &out, CYFlags flags) const {
1120 out << "this";
1121}
1122
1123namespace cy {
1124namespace Syntax {
1125
1126void Throw::Output(CYOutput &out, CYFlags flags) const {
1127 out << "throw";
1128 if (value_ != NULL)
1129 out << ' ' << *value_;
1130 out << ';';
1131}
1132
1133void Try::Output(CYOutput &out, CYFlags flags) const {
1134 out << "try" << ' ';
1135 out << '{' << '\n';
1136 ++out.indent_;
1137 out << code_;
1138 --out.indent_;
1139 out << '\t' << '}';
1140 out << catch_ << finally_;
1141}
1142
1143} }
1144
1145void CYTypeCharacter::Output(CYOutput &out) const {
1146 switch (signing_) {
1147 case CYTypeNeutral: break;
1148 case CYTypeSigned: out << "signed" << ' '; break;
1149 case CYTypeUnsigned: out << "unsigned" << ' '; break;
1150 }
1151
1152 out << "char";
1153}
1154
1155void CYTypeEnum::Output(CYOutput &out) const {
1156 out << "enum" << ' ';
1157 if (name_ != NULL)
1158 out << *name_;
1159 else {
1160 if (specifier_ != NULL)
1161 out << ':' << ' ' << *specifier_ << ' ';
1162
1163 out << '{' << '\n';
1164 ++out.indent_;
1165 bool comma(false);
1166
1167 CYForEach (constant, constants_) {
1168 if (comma)
1169 out << ',' << '\n';
1170 else
1171 comma = true;
1172 out << '\t' << constant->name_;
1173 out << ' ' << '=' << ' ' << constant->value_;
1174 }
1175
1176 if (out.pretty_)
1177 out << ',';
1178 out << '\n';
1179 --out.indent_;
1180 out << '\t' << '}';
1181 }
1182}
1183
1184void CYTypeError::Output(CYOutput &out) const {
1185 out << "@error";
1186}
1187
1188void CYTypeInt128::Output(CYOutput &out) const {
1189 switch (signing_) {
1190 case CYTypeNeutral: break;
1191 case CYTypeSigned: out << "signed" << ' '; break;
1192 case CYTypeUnsigned: out << "unsigned" << ' '; break;
1193 }
1194
1195 out << "__int128";
1196}
1197
1198void CYTypeIntegral::Output(CYOutput &out) const {
1199 if (signing_ == CYTypeUnsigned)
1200 out << "unsigned" << ' ';
1201 switch (length_) {
1202 case 0: out << "short"; break;
1203 case 1: out << "int"; break;
1204 case 2: out << "long"; break;
1205 case 3: out << "long" << ' ' << "long"; break;
1206 default: _assert(false);
1207 }
1208}
1209
1210void CYTypeStruct::Output(CYOutput &out) const {
1211 out << "struct";
1212 if (name_ != NULL)
1213 out << ' ' << *name_;
1214 else
1215 out << *tail_;
1216}
1217
1218void CYTypeReference::Output(CYOutput &out) const {
1219 switch (kind_) {
1220 case CYTypeReferenceStruct: out << "struct"; break;
1221 case CYTypeReferenceEnum: out << "enum"; break;
1222 default: _assert(false);
1223 }
1224
1225 out << ' ' << *name_;
1226}
1227
1228void CYTypeVariable::Output(CYOutput &out) const {
1229 out << *name_;
1230}
1231
1232void CYTypeVoid::Output(CYOutput &out) const {
1233 out << "void";
1234}
1235
1236void CYVar::Output(CYOutput &out, CYFlags flags) const {
1237 out << "var" << ' ';
1238 bindings_->Output(out, flags); // XXX: flags
1239 out << ';';
1240}
1241
1242void CYVariable::Output(CYOutput &out, CYFlags flags) const {
1243 out << *name_;
1244}
1245
1246void CYWhile::Output(CYOutput &out, CYFlags flags) const {
1247 out << "while" << ' ' << '(' << *test_ << ')';
1248 code_->Single(out, CYRight(flags), CYCompactShort);
1249}
1250
1251void CYWith::Output(CYOutput &out, CYFlags flags) const {
1252 out << "with" << ' ' << '(' << *scope_ << ')';
1253 code_->Single(out, CYRight(flags), CYCompactShort);
1254}
1255
1256void CYWord::Output(CYOutput &out) const {
1257 out << Word();
1258 if (out.options_.verbose_) {
1259 out('@');
1260 char number[32];
1261 sprintf(number, "%p", this);
1262 out(number);
1263 }
1264}
1265
1266void CYWord::PropertyName(CYOutput &out) const {
1267 Output(out);
1268}
1269
1270const char *CYWord::Word() const {
1271 return word_;
1272}