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