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