/src/CoffeeScript/yy/Class.php

https://gitlab.com/oytunistrator/coffeescript-php · PHP · 277 lines · 222 code · 55 blank · 0 comment · 46 complexity · 66347a9abf7a62a0cedcfb9d59affb53 MD5 · raw file

  1. <?php
  2. namespace CoffeeScript;
  3. class yy_Class extends yy_Base
  4. {
  5. public $children = array('variable', 'parent', 'body');
  6. function constructor($variable = NULL, $parent = NULL, $body = NULL)
  7. {
  8. $this->variable = $variable;
  9. $this->parent = $parent;
  10. $this->body = $body === NULL ? yy('Block') : $body;
  11. $this->body->class_body = TRUE;
  12. $this->bound_funcs = array();
  13. return $this;
  14. }
  15. function add_bound_functions($options)
  16. {
  17. if ($this->bound_funcs)
  18. {
  19. foreach ($this->bound_funcs as $bvar)
  20. {
  21. $lhs = yy('Value', yy('Literal', 'this'), array(yy('Access', $bvar)))->compile($options);
  22. $this->ctor->body->unshift(yy('Literal', "{$lhs} = ".utility('bind')."({$lhs}, this)"));
  23. }
  24. }
  25. }
  26. function add_properties($node, $name, $options)
  27. {
  28. $props = array_slice($node->base->properties, 0);
  29. $exprs = array();
  30. while ($assign = array_shift($props))
  31. {
  32. if ($assign instanceof yy_Assign)
  33. {
  34. $base = $assign->variable->base;
  35. $func = $assign->value;
  36. $assign->context = NULL;
  37. if ($base->value === 'constructor')
  38. {
  39. if ($this->ctor)
  40. {
  41. throw new Error('cannot define more than one constructor in a class');
  42. }
  43. if (isset($func->bound) && $func->bound)
  44. {
  45. throw new Error('cannot define a constructor as a bound functions');
  46. }
  47. if ($func instanceof yy_Code)
  48. {
  49. $assign = $this->ctor = $func;
  50. }
  51. else
  52. {
  53. $this->external_ctor = $options['scope']->free_variable('class');
  54. $assign = yy('Assign', yy('Literal', $this->external_ctor), $func);
  55. }
  56. }
  57. else
  58. {
  59. if (isset($assign->variable->this) && $assign->variable->this)
  60. {
  61. $func->static = TRUE;
  62. if (isset($func->bound) && $func->bound)
  63. {
  64. $func->context = $name;
  65. }
  66. }
  67. else
  68. {
  69. $assign->variable = yy('Value', yy('Literal', $name), array( yy('Access', yy('Literal', 'prototype')), yy('Access', $base) ));
  70. if ($func instanceof yy_Code && isset($func->bound) && $func->bound)
  71. {
  72. $this->bound_funcs[] = $base;
  73. $func->bound = FALSE;
  74. }
  75. }
  76. }
  77. }
  78. $exprs[] = $assign;
  79. }
  80. return compact($exprs);
  81. }
  82. function compile_node($options)
  83. {
  84. $decl = $this->determine_name();
  85. $name = $decl ? $decl : '_Class';
  86. if (isset($name->reserved) && $name->reserved)
  87. {
  88. $name = '_'.$name;
  89. }
  90. $lname = yy('Literal', $name);
  91. $this->hoist_directive_prologue();
  92. $this->set_context($name);
  93. $this->walk_body($name, $options);
  94. $this->ensure_constructor($name);
  95. $this->body->spaced = TRUE;
  96. if ( ! ($this->ctor instanceof yy_Code))
  97. {
  98. array_unshift($this->body->expressions, $this->ctor);
  99. }
  100. $this->body->expressions[] = $lname;
  101. $this->body->expressions = array_merge($this->directives, $this->body->expressions);
  102. $this->add_bound_functions($options);
  103. $call = yy_Closure::wrap($this->body);
  104. if ($this->parent)
  105. {
  106. $this->super_class = yy('Literal', $options['scope']->free_variable('super', FALSE));
  107. array_unshift($this->body->expressions, yy('Extends', $lname, $this->super_class));
  108. $call->args[] = $this->parent;
  109. if (isset($call->variable->params))
  110. {
  111. $params = & $call->variable->params;
  112. }
  113. else
  114. {
  115. $params = & $call->variable->base->params;
  116. }
  117. $params[] = yy('Param', $this->super_class);
  118. }
  119. $klass = yy('Parens', $call, TRUE);
  120. if ($this->variable)
  121. {
  122. $klass = yy('Assign', $this->variable, $klass);
  123. }
  124. return $klass->compile($options);
  125. }
  126. function determine_name()
  127. {
  128. if ( ! (isset($this->variable) && $this->variable))
  129. {
  130. return NULL;
  131. }
  132. if (($tail = last($this->variable->properties)))
  133. {
  134. $decl = $tail instanceof yy_Access ? $tail->name->value : NULL;
  135. }
  136. else
  137. {
  138. $decl = $this->variable->base->value;
  139. }
  140. if (in_array($decl, Lexer::$STRICT_PROSCRIBED, TRUE))
  141. {
  142. throw new SyntaxError("variable name may not be $decl");
  143. }
  144. $decl = $decl ? (preg_match(IDENTIFIER, $decl) ? $decl : NULL) : NULL;
  145. return $decl;
  146. }
  147. function ensure_constructor($name)
  148. {
  149. if ( ! (isset($this->ctor) && $this->ctor))
  150. {
  151. $this->ctor = yy('Code');
  152. if ($this->parent)
  153. {
  154. $this->ctor->body->push(yy('Literal', "{$name}.__super__.constructor.apply(this, arguments)"));
  155. }
  156. if (isset($this->external_ctor) && $this->external_ctor)
  157. {
  158. $this->ctor->body->push(yy('Literal', "{$this->external_ctor}.apply(this, arguments)"));
  159. }
  160. $this->ctor->body->make_return();
  161. array_unshift($this->body->expressions, $this->ctor);
  162. }
  163. $this->ctor->ctor = $this->ctor->name = $name;
  164. $this->ctor->klass = NULL;
  165. $this->ctor->no_return = TRUE;
  166. }
  167. function hoist_directive_prologue()
  168. {
  169. $index = 0;
  170. $expressions = $this->body->expressions;
  171. while (isset($expressions[$index]) && ($node = $expressions[$index]) && ( ($node instanceof yy_Comment) || ($node instanceof yy_Value) && $node->is_string() ))
  172. {
  173. $index++;
  174. }
  175. $this->directives = array_slice($expressions, 0, $index);
  176. }
  177. function set_context($name)
  178. {
  179. $this->body->traverse_children(FALSE, function($node) use ($name)
  180. {
  181. if (isset($node->class_body) && $node->class_body)
  182. {
  183. return FALSE;
  184. }
  185. if ($node instanceof yy_Literal && ''.$node->value === 'this')
  186. {
  187. $node->value = $name;
  188. }
  189. else if ($node instanceof yy_Code)
  190. {
  191. $node->klass = $name;
  192. if ($node->bound)
  193. {
  194. $node->context = $name;
  195. }
  196. }
  197. });
  198. }
  199. function walk_body($name, $options)
  200. {
  201. $self = $this;
  202. $this->traverse_children(FALSE, function($child) use ($name, $options, & $self)
  203. {
  204. if ($child instanceof yy_Class)
  205. {
  206. return FALSE;
  207. }
  208. if ($child instanceof yy_Block)
  209. {
  210. foreach (($exps = $child->expressions) as $i => $node)
  211. {
  212. if ($node instanceof yy_Value && $node->is_object(TRUE))
  213. {
  214. $exps[$i] = $self->add_properties($node, $name, $options);
  215. }
  216. }
  217. $child->expressions = $exps = flatten($exps);
  218. }
  219. });
  220. }
  221. }
  222. ?>