]> git.saurik.com Git - cycript.git/blob - Output.cpp
Milestone #1: YUI has been destroyed, with room to spare.
[cycript.git] / Output.cpp
1 /* Cycript - Inlining/Optimizing JavaScript Compiler
2 * Copyright (C) 2009 Jay Freeman (saurik)
3 */
4
5 /* Modified BSD License {{{ */
6 /*
7 * Redistribution and use in source and binary
8 * forms, with or without modification, are permitted
9 * provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the
12 * above copyright notice, this list of conditions
13 * and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the
15 * above copyright notice, this list of conditions
16 * and the following disclaimer in the documentation
17 * and/or other materials provided with the
18 * distribution.
19 * 3. The name of the author may not be used to endorse
20 * or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
25 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
34 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38 /* }}} */
39
40 #include "cycript.hpp"
41 #include "Parser.hpp"
42
43 #include <sstream>
44
45 _finline CYFlags operator ~(CYFlags rhs) {
46 return static_cast<CYFlags>(~static_cast<unsigned>(rhs));
47 }
48
49 _finline CYFlags operator &(CYFlags lhs, CYFlags rhs) {
50 return static_cast<CYFlags>(static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs));
51 }
52
53 _finline CYFlags operator |(CYFlags lhs, CYFlags rhs) {
54 return static_cast<CYFlags>(static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs));
55 }
56
57 _finline CYFlags &operator |=(CYFlags &lhs, CYFlags rhs) {
58 return lhs = lhs | rhs;
59 }
60
61 _finline CYFlags CYLeft(CYFlags flags) {
62 return flags & ~(CYNoDangle | CYNoInteger);
63 }
64
65 _finline CYFlags CYRight(CYFlags flags) {
66 return flags & ~CYNoBF;
67 }
68
69 _finline CYFlags CYCenter(CYFlags flags) {
70 return CYLeft(CYRight(flags));
71 }
72
73 void CYOutput::Terminate() {
74 out_ << ';';
75 mode_ = NoMode;
76 }
77
78 CYOutput &CYOutput::operator <<(char rhs) {
79 if (rhs == ' ' || rhs == '\n')
80 if (pretty_)
81 out_ << rhs;
82 else goto done;
83 else if (rhs == '\t')
84 if (pretty_)
85 for (unsigned i(0); i != indent_; ++i)
86 out_ << " ";
87 else goto done;
88 else if (rhs == '\r') {
89 if (right_) {
90 out_ << '\n';
91 right_ = false;
92 } goto done;
93 } else goto work;
94
95 right_ = true;
96 mode_ = NoMode;
97 goto done;
98
99 work:
100 if (mode_ == Terminated && rhs != '}') {
101 right_ = true;
102 out_ << ';';
103 }
104
105 if (rhs == ';') {
106 if (pretty_)
107 goto none;
108 else {
109 mode_ = Terminated;
110 goto done;
111 }
112 } else if (rhs == '+') {
113 if (mode_ == NoPlus)
114 out_ << ' ';
115 mode_ = NoPlus;
116 } else if (rhs == '-') {
117 if (mode_ == NoHyphen)
118 out_ << ' ';
119 mode_ = NoHyphen;
120 } else if (WordEndRange_[rhs]) {
121 if (mode_ == NoLetter)
122 out_ << ' ';
123 mode_ = NoLetter;
124 } else none:
125 mode_ = NoMode;
126
127 right_ = true;
128 out_ << rhs;
129 done:
130 return *this;
131 }
132
133 CYOutput &CYOutput::operator <<(const char *rhs) {
134 size_t size(strlen(rhs));
135
136 if (size == 1)
137 return *this << *rhs;
138
139 if (mode_ == Terminated)
140 out_ << ';';
141 else if (
142 mode_ == NoPlus && *rhs == '+' ||
143 mode_ == NoHyphen && *rhs == '-' ||
144 mode_ == NoLetter && WordEndRange_[*rhs]
145 )
146 out_ << ' ';
147
148 if (WordEndRange_[rhs[size - 1]])
149 mode_ = NoLetter;
150 else
151 mode_ = NoMode;
152
153 right_ = true;
154 out_ << rhs;
155 return *this;
156 }
157
158 void CYArgument::Output(CYOutput &out) const {
159 if (name_ != NULL) {
160 out << *name_;
161 if (value_ != NULL)
162 out << ':' << ' ';
163 }
164 if (value_ != NULL)
165 value_->Output(out, CYPA, CYNoFlags);
166 if (next_ != NULL) {
167 if (next_->name_ == NULL)
168 out << ',';
169 out << ' ' << *next_;
170 }
171 }
172
173 void CYArray::Output(CYOutput &out, CYFlags flags) const {
174 out << '[' << elements_ << ']';
175 }
176
177 void CYArrayComprehension::Output(CYOutput &out, CYFlags flags) const {
178 out << '[' << *expression_ << ' ' << *comprehensions_ << ']';
179 }
180
181 void CYAssignment::Output(CYOutput &out, CYFlags flags) const {
182 lhs_->Output(out, Precedence() - 1, CYLeft(flags) | CYNoRightHand);
183 out << ' ' << Operator() << ' ';
184 rhs_->Output(out, Precedence(), CYRight(flags));
185 }
186
187 void CYBlock::Output(CYOutput &out) const {
188 out << '{' << '\n';
189 ++out.indent_;
190 if (statements_ != NULL)
191 statements_->Multiple(out);
192 --out.indent_;
193 out << '\t' << '}';
194 }
195
196 void CYBlock::Output(CYOutput &out, CYFlags flags) const {
197 if (statements_ == NULL)
198 out.Terminate();
199 else if (statements_->next_ == NULL)
200 statements_->Single(out, flags);
201 else
202 Output(out);
203 }
204
205 void CYBoolean::Output(CYOutput &out, CYFlags flags) const {
206 out << (Value() ? "true" : "false");
207 }
208
209 void CYBreak::Output(CYOutput &out, CYFlags flags) const {
210 out << "break";
211 if (label_ != NULL)
212 out << ' ' << *label_;
213 out << ';';
214 }
215
216 void CYCall::Output(CYOutput &out, CYFlags flags) const {
217 bool protect((flags & CYNoCall) != 0);
218 if (protect)
219 out << '(';
220 function_->Output(out, Precedence(), protect ? CYNoFlags : flags);
221 out << '(' << arguments_ << ')';
222 if (protect)
223 out << ')';
224 }
225
226 namespace cy {
227 namespace Syntax {
228
229 void Catch::Output(CYOutput &out) const {
230 out << ' ' << "catch" << ' ' << '(' << *name_ << ')' << ' ' << code_;
231 }
232
233 } }
234
235 void CYComment::Output(CYOutput &out, CYFlags flags) const {
236 out << '\r';
237 out.out_ << value_;
238 out.right_ = true;
239 out << '\r';
240 }
241
242 void CYCompound::Output(CYOutput &out, CYFlags flags) const {
243 if (CYExpression *expression = expressions_)
244 if (CYExpression *next = expression->next_) {
245 expression->Output(out, CYLeft(flags));
246 CYFlags center(CYCenter(flags));
247 while (next != NULL) {
248 expression = next;
249 out << ',' << ' ';
250 next = expression->next_;
251 CYFlags right(next != NULL ? center : CYRight(flags));
252 expression->Output(out, right);
253 }
254 } else
255 expression->Output(out, flags);
256 }
257
258 void CYCondition::Output(CYOutput &out, CYFlags flags) const {
259 test_->Output(out, Precedence() - 1, CYLeft(flags));
260 out << ' ' << '?' << ' ';
261 if (true_ != NULL)
262 true_->Output(out, CYPA, CYNoFlags);
263 out << ' ' << ':' << ' ';
264 false_->Output(out, CYPA, CYRight(flags));
265 }
266
267 void CYContinue::Output(CYOutput &out, CYFlags flags) const {
268 out << "continue";
269 if (label_ != NULL)
270 out << ' ' << *label_;
271 out << ';';
272 }
273
274 void CYClause::Output(CYOutput &out) const {
275 if (case_ != NULL)
276 out << "case" << ' ' << *case_;
277 else
278 out << "default";
279 out << ':' << '\n';
280 if (statements_ != NULL)
281 statements_->Multiple(out);
282 out << next_;
283 }
284
285 const char *CYDeclaration::ForEachIn() const {
286 return identifier_->Word();
287 }
288
289 void CYDeclaration::ForIn(CYOutput &out, CYFlags flags) const {
290 out << "var";
291 Output(out, CYRight(flags));
292 }
293
294 void CYDeclaration::Output(CYOutput &out, CYFlags flags) const {
295 out << *identifier_;
296 if (initialiser_ != NULL) {
297 out << ' ' << '=' << ' ';
298 initialiser_->Output(out, CYPA, CYRight(flags));
299 }
300 }
301
302 void CYDeclarations::For(CYOutput &out) const {
303 out << "var";
304 Output(out, CYNoIn);
305 }
306
307 void CYDeclarations::Output(CYOutput &out) const {
308 Output(out, CYNoFlags);
309 }
310
311 void CYDeclarations::Output(CYOutput &out, CYFlags flags) const {
312 const CYDeclarations *declaration(this);
313 bool first(true);
314 output:
315 CYDeclarations *next(declaration->next_);
316 CYFlags jacks(first ? CYLeft(flags) : next == NULL ? CYRight(flags) : CYCenter(flags));
317 first = false;
318 declaration->declaration_->Output(out, jacks);
319
320 if (next != NULL) {
321 out << ',' << ' ';
322 declaration = next;
323 goto output;
324 }
325 }
326
327 void CYDirectMember::Output(CYOutput &out, CYFlags flags) const {
328 object_->Output(out, Precedence(), CYLeft(flags));
329 if (const char *word = property_->Word())
330 out << '.' << word;
331 else
332 out << '[' << *property_ << ']';
333 }
334
335 void CYDoWhile::Output(CYOutput &out, CYFlags flags) const {
336 out << "do";
337 code_->Single(out, CYCenter(flags));
338 out << "while" << ' ' << '(' << *test_ << ')';
339 }
340
341 void CYElement::Output(CYOutput &out) const {
342 if (value_ != NULL)
343 value_->Output(out, CYPA, CYNoFlags);
344 if (next_ != NULL || value_ == NULL) {
345 out << ',';
346 if (next_ != NULL && next_->value_ != NULL)
347 out << ' ';
348 }
349 if (next_ != NULL)
350 next_->Output(out);
351 }
352
353 void CYEmpty::Output(CYOutput &out, CYFlags flags) const {
354 out.Terminate();
355 }
356
357 void CYExpress::Output(CYOutput &out, CYFlags flags) const {
358 expression_->Output(out, flags | CYNoBF);
359 out << ';';
360 }
361
362 void CYExpression::ClassName(CYOutput &out, bool object) const {
363 Output(out, CYPA, CYNoFlags);
364 }
365
366 const char *CYExpression::ForEachIn() const {
367 return NULL;
368 }
369
370 void CYExpression::For(CYOutput &out) const {
371 Output(out, CYNoIn);
372 }
373
374 void CYExpression::ForIn(CYOutput &out, CYFlags flags) const {
375 Output(out, flags | CYNoRightHand);
376 }
377
378 void CYExpression::Output(CYOutput &out) const {
379 Output(out, CYNoFlags);
380 }
381
382 void CYExpression::Output(CYOutput &out, unsigned precedence, CYFlags flags) const {
383 if (precedence < Precedence() || (flags & CYNoRightHand) != 0 && RightHand())
384 out << '(' << *this << ')';
385 else
386 Output(out, flags);
387 }
388
389 void CYFinally::Output(CYOutput &out) const {
390 out << ' ' << "finally" << ' ' << code_;
391 }
392
393 void CYFor::Output(CYOutput &out, CYFlags flags) const {
394 out << "for" << ' ' << '(';
395 if (initialiser_ != NULL)
396 initialiser_->For(out);
397 out.Terminate();
398 out << test_;
399 out.Terminate();
400 out << increment_;
401 out << ')';
402 code_->Single(out, CYRight(flags));
403 }
404
405 void CYForEachIn::Output(CYOutput &out, CYFlags flags) const {
406 out << "for" << ' ' << "each" << ' ' << '(';
407 initialiser_->ForIn(out, CYNoIn);
408 out << "in" << *set_ << ')';
409 code_->Single(out, CYRight(flags));
410 }
411
412 void CYForEachInComprehension::Output(CYOutput &out) const {
413 out << "for" << ' ' << "each" << ' ' << '(' << *name_ << ' ' << "in" << ' ' << *set_ << ')' << next_;
414 }
415
416 void CYForIn::Output(CYOutput &out, CYFlags flags) const {
417 out << "for" << ' ' << '(';
418 if (initialiser_ != NULL)
419 initialiser_->ForIn(out, CYNoIn);
420 out << "in" << *set_ << ')';
421 code_->Single(out, CYRight(flags));
422 }
423
424 void CYForInComprehension::Output(CYOutput &out) const {
425 out << "for" << ' ' << '(' << *name_ << ' ' << "in" << ' ' << *set_ << ')';
426 }
427
428 void CYFunction::Output(CYOutput &out, CYFlags flags) const {
429 // XXX: one could imagine using + here to save a byte
430 bool protect((flags & CYNoFunction) != 0);
431 if (protect)
432 out << '(';
433 out << "function";
434 if (out.options_.verbose_)
435 out.out_ << ':' << static_cast<const CYScope *>(this);
436 if (name_ != NULL)
437 out << ' ' << *name_;
438 out << '(' << parameters_ << ')';
439 out << ' ' << code_;
440 if (protect)
441 out << ')';
442 }
443
444 void CYFunctionExpression::Output(CYOutput &out, CYFlags flags) const {
445 CYFunction::Output(out, flags);
446 }
447
448 void CYFunctionStatement::Output(CYOutput &out, CYFlags flags) const {
449 CYFunction::Output(out, flags);
450 }
451
452 void CYFunctionParameter::Output(CYOutput &out) const {
453 out << *name_;
454 if (next_ != NULL)
455 out << ',' << ' ' << *next_;
456 }
457
458 const char *CYIdentifier::Word() const {
459 return replace_ == NULL || replace_ == this ? CYWord::Word() : replace_->Word();
460 }
461
462 void CYIf::Output(CYOutput &out, CYFlags flags) const {
463 bool protect(false);
464 if (false_ == NULL && (flags & CYNoDangle) != 0) {
465 protect = true;
466 out << '{';
467 }
468
469 out << "if" << ' ' << '(' << *test_ << ')';
470
471 CYFlags right(protect ? CYNoFlags : CYRight(flags));
472
473 CYFlags jacks(CYNoDangle);
474 if (false_ == NULL)
475 jacks |= right;
476 else
477 jacks |= protect ? CYNoFlags : CYCenter(flags);
478
479 true_->Single(out, jacks);
480
481 if (false_ != NULL) {
482 out << "else";
483 false_->Single(out, right);
484 }
485
486 if (protect)
487 out << '}';
488 }
489
490 void CYIfComprehension::Output(CYOutput &out) const {
491 out << "if" << ' ' << '(' << *test_ << ')' << next_;
492 }
493
494 void CYIndirectMember::Output(CYOutput &out, CYFlags flags) const {
495 object_->Output(out, Precedence(), CYLeft(flags));
496 if (const char *word = property_->Word())
497 out << "->" << word;
498 else
499 out << "->" << '[' << *property_ << ']';
500 }
501
502 void CYInfix::Output(CYOutput &out, CYFlags flags) const {
503 const char *name(Operator());
504 bool protect((flags & CYNoIn) != 0 && strcmp(name, "in") == 0);
505 if (protect)
506 out << '(';
507 CYFlags left(protect ? CYNoFlags : CYLeft(flags));
508 lhs_->Output(out, Precedence(), left);
509 out << ' ' << name << ' ';
510 CYFlags right(protect ? CYNoFlags : CYRight(flags));
511 rhs_->Output(out, Precedence() - 1, right);
512 if (protect)
513 out << ')';
514 }
515
516 void CYLabel::Output(CYOutput &out, CYFlags flags) const {
517 out << *name_ << ':' << ' ';
518 statement_->Single(out, CYRight(flags));
519 }
520
521 void CYLet::Output(CYOutput &out, CYFlags flags) const {
522 out << "let" << ' ' << '(' << *declarations_ << ')' << ' ' << code_;
523 }
524
525 void CYNew::Output(CYOutput &out, CYFlags flags) const {
526 out << "new" << ' ';
527 CYFlags jacks(CYNoCall | CYCenter(flags));
528 constructor_->Output(out, Precedence(), jacks);
529 if (arguments_ != NULL)
530 out << '(' << *arguments_ << ')';
531 }
532
533 void CYNull::Output(CYOutput &out, CYFlags flags) const {
534 CYWord::Output(out);
535 }
536
537 void CYNumber::Output(CYOutput &out, CYFlags flags) const {
538 std::ostringstream str;
539 CYNumerify(str, Value());
540 std::string value(str.str());
541 out << value.c_str();
542 // XXX: this should probably also handle hex conversions and exponents
543 if ((flags & CYNoInteger) != 0 && value.find('.') == std::string::npos)
544 out << '.';
545 }
546
547 void CYNumber::PropertyName(CYOutput &out) const {
548 Output(out, CYNoFlags);
549 }
550
551 void CYObject::Output(CYOutput &out, CYFlags flags) const {
552 bool protect((flags & CYNoBrace) != 0);
553 if (protect)
554 out << '(';
555 out << '{' << '\n';
556 ++out.indent_;
557 out << properties_;
558 --out.indent_;
559 out << '\t' << '}';
560 if (protect)
561 out << ')';
562 }
563
564 void CYPostfix::Output(CYOutput &out, CYFlags flags) const {
565 lhs_->Output(out, Precedence(), CYLeft(flags));
566 out << Operator();
567 }
568
569 void CYPrefix::Output(CYOutput &out, CYFlags flags) const {
570 const char *name(Operator());
571 out << name;
572 if (Alphabetic())
573 out << ' ';
574 rhs_->Output(out, Precedence(), CYRight(flags));
575 }
576
577 void CYProgram::Output(CYOutput &out) const {
578 if (statements_ != NULL)
579 statements_->Multiple(out);
580 }
581
582 void CYProperty::Output(CYOutput &out) const {
583 out << '\t';
584 name_->PropertyName(out);
585 out << ':' << ' ';
586 value_->Output(out, CYPA, CYNoFlags);
587 if (next_ != NULL)
588 out << ',' << '\n' << *next_;
589 else
590 out << '\n';
591 }
592
593 void CYRegEx::Output(CYOutput &out, CYFlags flags) const {
594 out << Value();
595 }
596
597 void CYReturn::Output(CYOutput &out, CYFlags flags) const {
598 out << "return";
599 if (value_ != NULL)
600 out << ' ' << *value_;
601 out << ';';
602 }
603
604 void CYStatement::Multiple(CYOutput &out, CYFlags flags) const {
605 bool first(true);
606 for (const CYStatement *next(this); next != NULL; next = next->next_) {
607 bool last(next->next_ == NULL);
608 CYFlags jacks(first ? last ? flags : CYLeft(flags) : last ? CYCenter(flags) : CYRight(flags));
609 first = false;
610 out << '\t';
611 next->Output(out, jacks);
612 out << '\n';
613 }
614 }
615
616 void CYStatement::Single(CYOutput &out, CYFlags flags) const {
617 _assert(next_ == NULL);
618 out << '\n';
619 ++out.indent_;
620 out << '\t';
621 Output(out, flags);
622 out << '\n';
623 --out.indent_;
624 }
625
626 void CYString::Output(CYOutput &out, CYFlags flags) const {
627 std::ostringstream str;
628 CYStringify(str, value_, size_);
629 out << str.str().c_str();
630 }
631
632 void CYString::PropertyName(CYOutput &out) const {
633 if (const char *word = Word())
634 out << word;
635 else
636 out << *this;
637 }
638
639 static const char *Reserved_[] = {
640 "false", "null", "true",
641
642 "break", "case", "catch", "continue", "default",
643 "delete", "do", "else", "finally", "for", "function",
644 "if", "in", "instanceof", "new", "return", "switch",
645 "this", "throw", "try", "typeof", "var", "void",
646 "while", "with",
647
648 "debugger", "const",
649
650 "class", "enum", "export", "extends", "import", "super",
651
652 "abstract", "boolean", "byte", "char", "double", "final",
653 "float", "goto", "int", "long", "native", "short",
654 "synchronized", "throws", "transient", "volatile",
655
656 "let", "yield",
657
658 "each",
659
660 NULL
661 };
662
663 const char *CYString::Word() const {
664 if (size_ == 0 || !WordStartRange_[value_[0]])
665 return NULL;
666 for (size_t i(1); i != size_; ++i)
667 if (!WordEndRange_[value_[i]])
668 return NULL;
669 const char *value(Value());
670 for (const char **reserved(Reserved_); *reserved != NULL; ++reserved)
671 if (strcmp(*reserved, value) == 0)
672 return NULL;
673 return value;
674 }
675
676 void CYSwitch::Output(CYOutput &out, CYFlags flags) const {
677 out << "switch" << ' ' << '(' << *value_ << ')' << ' ' << '{';
678 out << clauses_;
679 out << '}';
680 }
681
682 void CYThis::Output(CYOutput &out, CYFlags flags) const {
683 CYWord::Output(out);
684 }
685
686 namespace cy {
687 namespace Syntax {
688
689 void Throw::Output(CYOutput &out, CYFlags flags) const {
690 out << "throw";
691 if (value_ != NULL)
692 out << ' ' << *value_;
693 out << ';';
694 }
695
696 void Try::Output(CYOutput &out, CYFlags flags) const {
697 out << "try" << ' ' << code_ << catch_ << finally_;
698 }
699
700 } }
701
702 void CYVar::Output(CYOutput &out, CYFlags flags) const {
703 out << "var";
704 declarations_->Output(out, flags);
705 out << ';';
706 }
707
708 void CYVariable::Output(CYOutput &out, CYFlags flags) const {
709 out << *name_;
710 }
711
712 void CYWhile::Output(CYOutput &out, CYFlags flags) const {
713 out << "while" << '(' << *test_ << ')';
714 code_->Single(out, CYRight(flags));
715 }
716
717 void CYWith::Output(CYOutput &out, CYFlags flags) const {
718 out << "with" << '(' << *scope_ << ')';
719 code_->Single(out, CYRight(flags));
720 }
721
722 void CYWord::ClassName(CYOutput &out, bool object) const {
723 if (object)
724 out << "objc_getClass(";
725 out << '"' << Word() << '"';
726 if (object)
727 out << ')';
728 }
729
730 void CYWord::Output(CYOutput &out) const {
731 out << Word();
732 if (out.options_.verbose_)
733 out.out_ << '@' << this;
734 }
735
736 void CYWord::PropertyName(CYOutput &out) const {
737 Output(out);
738 }
739
740 const char *CYWord::Word() const {
741 return word_;
742 }