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