PageRenderTime 62ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/libfructose/libfructose.php

http://github.com/charliesome/Fructose
PHP | 2402 lines | 2270 code | 92 blank | 40 comment | 213 complexity | 47347af1730b8bf9d5b2fb661d46ed25 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. Copyright (c) 2011 Charlie Somerville
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source
  17. distribution.
  18. */
  19. $GLOBALS['_stack'] = array();
  20. $GLOBALS['_lambda_objs'] = array();
  21. $GLOBALS['_locals'] = new stdClass;
  22. $GLOBALS['_locals']->self = new F_Object;
  23. $GLOBALS['_gthis'] = $_locals->self;
  24. $GLOBALS['_globals'] = array();
  25. class ReturnFromBlock extends Exception
  26. {
  27. public $val;
  28. function __construct($val)
  29. {
  30. $this->val = $val;
  31. }
  32. }
  33. class ErrorCarrier extends Exception
  34. {
  35. public $val;
  36. function __construct($val)
  37. {
  38. $this->val = $val;
  39. }
  40. }
  41. function _exception_handler($ex)
  42. {
  43. header("HTTP/1.1 500 Internal Server Error");
  44. ?>
  45. <!DOCTYPE html>
  46. <html>
  47. <head>
  48. <title>Runtime Error</title>
  49. <style>
  50. body {
  51. font-family:Tahoma, Verdana, Arial, Helvetica, sans-serif;
  52. }
  53. h1 {
  54. padding:16px;
  55. background-color:#ff8e55;
  56. color:#441700;
  57. }
  58. div {
  59. font-family:Consolas, Monaco, monospace;
  60. font-size:12px;
  61. line-height:18px;
  62. }
  63. </style>
  64. </head>
  65. <body>
  66. <h1>Runtime Error in <?php echo htmlspecialchars($_SERVER['FRUCTOSE_SRC']); ?></h1>
  67. <div>
  68. Unhandled exception: <?php echo htmlspecialchars(get_class($ex->val)); ?><br />
  69. <?php echo htmlspecialchars($ex->val->F_to_s(NULL)->__STRING); ?><br/>
  70. <?php
  71. foreach($ex->getTrace() as $frame)
  72. {
  73. echo "at " . htmlspecialchars($frame['function']) . ' in ' . htmlspecialchars($frame['file']) . ' on line ' . $frame['line'] . "<br />";
  74. }
  75. ?>
  76. </div>
  77. </body>
  78. </html>
  79. <?php
  80. exit;
  81. }
  82. set_exception_handler('_exception_handler');
  83. $_operator_lookup["!"] = "__operator_not";
  84. $_operator_lookup["~"] = "__operator_bitwisenot";
  85. $_operator_lookup["+@"] = "__operator_unaryplus";
  86. $_operator_lookup["**"] = "__operator_exp";
  87. $_operator_lookup["-@"] = "__operator_unaryminus";
  88. $_operator_lookup["*"] = "__operator_mul";
  89. $_operator_lookup["/"] = "__operator_div";
  90. $_operator_lookup["%"] = "__operator_mod";
  91. $_operator_lookup["+"] = "__operator_add";
  92. $_operator_lookup["-"] = "__operator_sub";
  93. $_operator_lookup["<<"] = "__operator_lshift";
  94. $_operator_lookup[">>"] = "__operator_rshift";
  95. $_operator_lookup["&"] = "__operator_bitwiseand";
  96. $_operator_lookup["|"] = "__operator_bitwiseor";
  97. $_operator_lookup["^"] = "__operator_xor";
  98. $_operator_lookup["<"] = "__operator_lt";
  99. $_operator_lookup["<="] = "__operator_lte";
  100. $_operator_lookup[">"] = "__operator_gt";
  101. $_operator_lookup[">="] = "__operator_gte";
  102. $_operator_lookup["=="] = "__operator_eq";
  103. $_operator_lookup["==="] = "__operator_stricteq";
  104. $_operator_lookup["!="] = "__operator_neq";
  105. $_operator_lookup["=~"] = "__operator_match";
  106. $_operator_lookup["!~"] = "__operator_notmatch";
  107. $_operator_lookup["<=>"] = "__operator_spaceship";
  108. $_operator_lookup["[]"] = "__operator_arrayget";
  109. $_operator_lookup["[]="] = "__operator_arrayset";
  110. function _rmethod_to_php($method)
  111. {
  112. global $_operator_lookup;
  113. if(isset($_operator_lookup[$method]))
  114. return $_operator_lookup[$method];
  115. return "F_" . str_replace("?", "_QUES_", str_replace("!", "_EXCL", str_replace("=", "__set", $method)));
  116. }
  117. function _isTruthy($obj)
  118. {
  119. $class = get_class($obj);
  120. return $class !== 'F_NilClass' && $class !== 'F_FalseClass';
  121. }
  122. class F_Object
  123. {
  124. public $_instance_vars = array();
  125. public $_dyn_methods = array();
  126. public static $_dyn_global_methods = array();
  127. public static $_class_vars = array();
  128. public $_tainted = FALSE;
  129. public $_untrusted = FALSE;
  130. public function F_to_s($block)
  131. {
  132. return $this->F_class(NULL)->F_to_s(NULL);
  133. }
  134. public function F_puts($block,$o)
  135. {
  136. $str = $o->F_to_s(NULL);
  137. if(_isTruthy($str->F_tainted_QUES_(NULL)))
  138. throw new ErrorCarrier(F_SecurityError::SF_new(NULL, F_String::__from_string("Attempted to puts tainted data")));
  139. echo $str->__STRING . "\n";
  140. return new F_NilClass;
  141. }
  142. public function F_require($block, $str)
  143. {
  144. $path = $str->F_to_s(NULL)->__STRING;
  145. foreach(array('%s','%s.php','%s.fruc.php','lib/%s','lib/%s.php','lib/%s.fruc.php') as $pathfmt)
  146. {
  147. $p = sprintf($pathfmt, $path);
  148. if(@file_get_contents($p, FILE_USE_INCLUDE_PATH) !== FALSE)
  149. {
  150. require_once $p;
  151. return new F_TrueClass;
  152. }
  153. }
  154. if(function_exists("_fructose_dynamic_include"))
  155. {
  156. foreach(explode(PATH_SEPARATOR, get_include_path()) as $path_root)
  157. {
  158. if(file_exists($path_root . "/" . $path))
  159. return _fructose_dynamic_include(realpath($path . ".fruc"));
  160. }
  161. }
  162. return new F_FalseClass;
  163. }
  164. public function F_class($block)
  165. {
  166. return F_Symbol::__from_string(get_class($this));
  167. }
  168. public function F_respond_to_QUES_($block, $sym, $include_private = NULL)
  169. {
  170. if($include_private === NULL)
  171. $include_private = new F_FalseClass;
  172. if(method_exists($this, _rmethod_to_php($sym->__SYMBOL)))
  173. return new F_TrueClass;
  174. if(method_exists($this, "F_respond_to_missing_QUES_"))
  175. return $this->F_respond_to_missing_QUES_($sym, $include_private);
  176. return new F_FalseClass;
  177. }
  178. public function F_send($block, $sym)
  179. {
  180. $args = func_get_args();
  181. array_splice($args, 1, 1);
  182. return call_user_func_array(array($this, _rmethod_to_php($sym->__SYMBOL)), $args);
  183. }
  184. public function __call($name, $args)
  185. {
  186. if(isset($this->_dyn_methods[$name]))
  187. return call_user_func_array($this->_dyn_methods[$name], $args);
  188. if(isset(F_Object::$_dyn_global_methods[$name]))
  189. return call_user_func_array(F_Object::$_dyn_global_methods[$name], $args);
  190. if(get_class($this) === 'F_Object' && function_exists($name))
  191. return call_user_func_array($name, $args);
  192. throw new ErrorCarrier(F_NoMethodError::SF_new(NULL, F_String::__from_string("No such method " . substr(get_class($this), 2) . "#" . substr($name, 2))));
  193. }
  194. public function F_define_method($block, $sym)
  195. {
  196. $this->_dyn_methods[_rmethod_to_php($sym->F_to_s(NULL)->__STRING)] = $block;
  197. }
  198. public function __add_method($name, $fn)
  199. {
  200. $this->_dyn_methods[$name] = $fn;
  201. }
  202. public function F_define_global_method($block, $sym)
  203. {
  204. F_Object::$_dyn_global_methods[_rmethod_to_php($sym->F_to_s(NULL)->__STRING)] = $block;
  205. return $this;
  206. }
  207. public static function __add_global_method($name, $fn)
  208. {
  209. F_Object::$_dyn_global_methods[$name] = $fn;
  210. return $this;
  211. }
  212. public function __operator_notmatch($block, $operand)
  213. {
  214. // foo ^ true == !foo
  215. return $this->__operator_match(NULL, $operand)->__operator_xor(new F_TrueClass);
  216. }
  217. public function __operator_stricteq($block, $operand)
  218. {
  219. return $this->__operator_eq(NULL, $operand);
  220. }
  221. public function __operator_neq($block, $operand)
  222. {
  223. return F_TrueClass::__from_bool(! _isTruthy($this->__operator_eq(NULL, $operand)));
  224. }
  225. public function F_clone($block)
  226. {
  227. $new = clone $this;
  228. if(_isTruthy($new->F_respond_to_QUES_(NULL, F_Symbol::__from_string("initialize_copy"))))
  229. $new->F_initialize_copy(NULL, $this);
  230. return $new;
  231. }
  232. public function F_dup($block)
  233. {
  234. $new = clone $this;
  235. if(_isTruthy($new->F_respond_to_QUES_(NULL, F_Symbol::__from_string("initialize_copy"))))
  236. $new->F_initialize_copy(NULL, $this);
  237. return $new;
  238. }
  239. public function __operator_eq($block, $operand)
  240. {
  241. return F_TrueClass::__from_bool($this === $operand);
  242. }
  243. public function F_equal_QUES_($block, $operand)
  244. {
  245. return F_TrueClass::__from_bool($this === $operand);
  246. }
  247. public function F_inspect($block)
  248. {
  249. return $this->F_to_s(NULL);
  250. }
  251. public function F_instance_of_QUES_($block, $sym)
  252. {
  253. return F_TrueClass::__from_bool(get_class($this) === _rmethod_to_php($sym->__SYMBOL));
  254. }
  255. public function F_is_a_QUES_($block, $sym)
  256. {
  257. return F_TrueClass::__from_bool(is_a($this, _rmethod_to_php($sym->__SYMBOL)));
  258. }
  259. public function F_nil_QUES_($block)
  260. {
  261. return new F_FalseClass;
  262. }
  263. public function F_taint($block)
  264. {
  265. $this->_tainted = TRUE;
  266. return $this;
  267. }
  268. public function F_untaint($block)
  269. {
  270. $this->_tainted = FALSE;
  271. return $this;
  272. }
  273. public function F_tainted_QUES_($block)
  274. {
  275. return F_TrueClass::__from_bool($this->_tainted);
  276. }
  277. public function F_tap($block)
  278. {
  279. $block(NULL, $this);
  280. return $this;
  281. }
  282. public function F_trust($block)
  283. {
  284. $this->_untrusted = FALSE;
  285. return $this;
  286. }
  287. public function F_untrust($block)
  288. {
  289. $this->_untrusted = TRUE;
  290. return $this;
  291. }
  292. public function F_untrusted_QUES_($block)
  293. {
  294. return F_TrueClass::__from_bool($this->_untrusted);
  295. }
  296. public function F_raise($block, $err)
  297. {
  298. if(is_a($err, 'F_Error'))
  299. throw new ErrorCarrier($err);
  300. throw new ErrorCarrier(F_Error::SF_new(NULL, $err));
  301. }
  302. }
  303. class F_Error extends F_Object
  304. {
  305. public static function SF_new($block, $msg = NULL)
  306. {
  307. $err = new F_Error;
  308. $err->__MESSAGE = $msg !== NULL ? $msg->F_to_s(NULL) : new F_NilClass;
  309. return $err;
  310. }
  311. public function F_message($block)
  312. {
  313. return $this->__MESSAGE;
  314. }
  315. public function F_message__set($block, $val)
  316. {
  317. return ($this->__MESSAGE = $val);
  318. }
  319. public function F_to_s($block)
  320. {
  321. return _isTruthy($this->__MESSAGE) ? $this->__MESSAGE : $this->F_class(NULL);
  322. }
  323. public function F_inspect($block)
  324. {
  325. $ex = $this->F_class(NULL)->F_to_s(NULL);
  326. $ex->__operator_lshift(NULL, F_String::__from_string(": "));
  327. $ex->__operator_lshift(NULL, $this->__MESSAGE);
  328. return $ex;
  329. }
  330. }
  331. class F_NoMethodError extends F_Error
  332. {
  333. public static function SF_new($block, $msg = NULL)
  334. {
  335. $err = new F_NoMethodError;
  336. $err->__MESSAGE = $msg !== NULL ? $msg->F_to_s(NULL) : new F_NilClass;
  337. return $err;
  338. }
  339. }
  340. class F_StopIteration extends F_Error
  341. {
  342. public static function SF_new($block, $msg = NULL)
  343. {
  344. $err = new F_StopIteration;
  345. $err->__MESSAGE = $msg !== NULL ? $msg->F_to_s(NULL) : new F_NilClass;
  346. return $err;
  347. }
  348. }
  349. class F_IOError extends F_Error
  350. {
  351. public static function SF_new($block, $msg = NULL)
  352. {
  353. $err = new F_IOError;
  354. $err->__MESSAGE = $msg !== NULL ? $msg->F_to_s(NULL) : new F_NilClass;
  355. return $err;
  356. }
  357. }
  358. class F_SecurityError extends F_Error
  359. {
  360. public static function SF_new($block, $msg = NULL)
  361. {
  362. $err = new F_SecurityError;
  363. $err->__MESSAGE = $msg !== NULL ? $msg->F_to_s(NULL) : new F_NilClass;
  364. return $err;
  365. }
  366. }
  367. class F_File extends F_Enumerable
  368. {
  369. public static function SF_new($block, $filename, $m = NULL)
  370. {
  371. $mode = $m === NULL ? "r" : $m->F_to_s(NULL)->__STRING;
  372. $file = new F_File;
  373. $file->__HANDLE = fopen($filename->F_to_s(NULL)->__STRING, $mode);
  374. $file->__CLOSED = FALSE;
  375. if($block === NULL)
  376. return $file;
  377. $block(NULL, $file);
  378. if(!$file->__CLOSED)
  379. $file->F_close(NULL);
  380. }
  381. public static function SF_absolute_path($block, $filename)
  382. {
  383. $p = realpath($filename->F_to_s(NULL)->__STRING);
  384. if($p === FALSE || $p === NULL)
  385. return new F_NilClass;
  386. return F_String::__from_string($p);
  387. }
  388. public static function SF_exist_QUES_($block, $filename)
  389. {
  390. return F_TrueClass::__from_bool(file_exists($filename->F_to_s(NULL)->__STRING));
  391. }
  392. public function F_close($block)
  393. {
  394. if($this->__CLOSED)
  395. throw new ErrorCarrier(F_IOError::SF_new(NULL, F_String::__from_string("File already closed")));
  396. fclose($this->__HANDLE);
  397. $file->__CLOSED = TRUE;
  398. return new F_NilClass;
  399. }
  400. public function F_closed_QUES_($block)
  401. {
  402. return F_TrueClass::__from_bool($this->__CLOSED);
  403. }
  404. public function __operator_lshift($block, $obj)
  405. {
  406. if($this->__CLOSED)
  407. throw new ErrorCarrier(F_IOError::SF_new(NULL, F_String::__from_string("File closed")));
  408. fwrite($this->__HANDLE, $obj->F_to_s(NULL)->__STRING);
  409. return $this;
  410. }
  411. public function F_each($block)
  412. {
  413. if($this->__CLOSED)
  414. throw new ErrorCarrier(F_IOError::SF_new(NULL, F_String::__from_string("File closed")));
  415. if($block !== NULL)
  416. {
  417. while(($line = fgets($this->__HANDLE)) !== FALSE)
  418. $block(NULL, F_String::__from_string($line));
  419. return new F_NilClass;
  420. }
  421. while(($line = fgets($this->__HANDLE)) !== FALSE)
  422. $lines[] = F_String::__from_string($line);
  423. return F_Enumerator::__from_array($lines);
  424. }
  425. public function F_eof_QUES_($block)
  426. {
  427. if($this->__CLOSED)
  428. throw new ErrorCarrier(F_IOError::SF_new(NULL, F_String::__from_string("File closed")));
  429. return F_TrueClass::__from_bool(feof($this->__HANDLE));
  430. }
  431. public function F_flush($block)
  432. {
  433. if($this->__CLOSED)
  434. throw new ErrorCarrier(F_IOError::SF_new(NULL, F_String::__from_string("File closed")));
  435. fflush($this->__HANDLE);
  436. return new F_NilClass;
  437. }
  438. public function F_getc($block)
  439. {
  440. if($this->__CLOSED)
  441. throw new ErrorCarrier(F_IOError::SF_new(NULL, F_String::__from_string("File closed")));
  442. if(feof($this->__HANDLE))
  443. return new F_NilClass;
  444. return F_String::__from_string(fgetc($this->__HANDLE));
  445. }
  446. public function F_gets($block)
  447. {
  448. if($this->__CLOSED)
  449. throw new ErrorCarrier(F_IOError::SF_new(NULL, F_String::__from_string("File closed")));
  450. if(feof($this->__HANDLE))
  451. return new F_NilClass;
  452. return F_String::__from_string(fgets($this->__HANDLE));
  453. }
  454. public function F_tell($block)
  455. {
  456. if($this->__CLOSED)
  457. throw new ErrorCarrier(F_IOError::SF_new(NULL, F_String::__from_string("File closed")));
  458. return F_Number::__from_number(ftell($this->__HANDLE));
  459. }
  460. public function F_read($block, $length, $buffer = NULL)
  461. {
  462. if($this->__CLOSED)
  463. throw new ErrorCarrier(F_IOError::SF_new(NULL, F_String::__from_string("File closed")));
  464. if(feof($this->__HANDLE))
  465. return new F_NilClass;
  466. $buff = F_String::__from_string(fread($this->__HANDLE, $length->__NUMBER));
  467. if($buffer !== NULL)
  468. {
  469. $buffer->__operator_lshift(NULL, $buff);
  470. return $buffer;
  471. }
  472. return $buff;
  473. }
  474. public function F_rewind($block)
  475. {
  476. if($this->__CLOSED)
  477. throw new ErrorCarrier(F_IOError::SF_new(NULL, F_String::__from_string("File closed")));
  478. fseek($this->__HANDLE, 0);
  479. return F_Number::__from_number(0);
  480. }
  481. public function F_seek($block, $offset, $whence = NULL)
  482. {
  483. if($this->__CLOSED)
  484. throw new ErrorCarrier(F_IOError::SF_new(NULL, F_String::__from_string("File closed")));
  485. $w = SEEK_SET;
  486. if(_isTruthy($whence->__operator_eq(NULL, F_Symbol::__from_string("cur"))))
  487. $w = SEEK_CUR;
  488. elseif(_isTruthy($whence->__operator_eq(NULL, F_Symbol::__from_string("end"))))
  489. $w = SEEK_END;
  490. fseek($this->__HANDLE, $offset->__NUMBER, $w);
  491. return F_Number::__from_number(0);
  492. }
  493. public function F_stat($block)
  494. {
  495. if($this->__CLOSED)
  496. throw new ErrorCarrier(F_IOError::SF_new(NULL, F_String::__from_string("File closed")));
  497. $stats = array();
  498. foreach(fstat($this->__HANDLE) as $k=>$v)
  499. {
  500. $stats[] = F_Symbol::__from_string($k);
  501. $stats[] = F_Number::__from_number($v);
  502. }
  503. return F_Hash::__from_flatpairs($stats);
  504. }
  505. public function F_write($block, $obj)
  506. {
  507. if($this->__CLOSED)
  508. throw new ErrorCarrier(F_IOError::SF_new(NULL, F_String::__from_string("File closed")));
  509. fwrite($this->__HANDLE, $obj->F_to_s(NULL)->__STRING);
  510. return $this;
  511. }
  512. }
  513. class F_Enumerator extends F_Object
  514. {
  515. public static function __from_array($array)
  516. {
  517. $e = new F_Enumerator;
  518. $e->__ARRAY = $array;
  519. $e->__INDEX = 0;
  520. return $e;
  521. }
  522. public function F_next($block)
  523. {
  524. if($this->__INDEX === count($this->__ARRAY))
  525. throw new ErrorCarrier(F_StopIteration::SF_new(NULL));
  526. return $this->__ARRAY[$this->__INDEX++];
  527. }
  528. public function F_peek($block)
  529. {
  530. return $this->__ARRAY[$this->__INDEX];
  531. }
  532. }
  533. class F_Proc extends F_Object
  534. {
  535. public static function SF_new($block)
  536. {
  537. $p = new F_Proc;
  538. $p->__BLOCK = $block;
  539. return $p;
  540. }
  541. public function __operator_eq($block, $operand)
  542. {
  543. return F_TrueClass::__from_bool($this->__BLOCK === $operand->__BLOCK);
  544. }
  545. public function __operator_stricteq($block, $operand)
  546. {
  547. $blockfn = $this->__BLOCK;
  548. return $blockfn(NULL, $operand);
  549. }
  550. public function F_call($block)
  551. {
  552. $args = func_get_args();
  553. return call_user_func_array($this->__BLOCK, $args);
  554. }
  555. public function F_to_s($block)
  556. {
  557. return F_String::__from_string($this->__BLOCK);
  558. }
  559. }
  560. class F_Random extends F_Object
  561. {
  562. static $seed = 0;
  563. public static function SF_seed($block, $seed = NULL)
  564. {
  565. $n = $seed !== NULL ? (int)$seed->__NUMBER : time();
  566. mt_srand($n);
  567. $old_seed = F_Random::$seed;
  568. F_Random::$seed = $n;
  569. return F_Number::__from_number($old_seed);
  570. }
  571. public static function SF_rand($max = NULL)
  572. {
  573. $m = $max !== NULL ? $max->__NUMBER : 1.0;
  574. return F_Number::__from_number((mt_rand() / mt_getrandmax()) * $m);
  575. }
  576. }
  577. class F_Regexp extends F_Object
  578. {
  579. public static $_matches = array();
  580. public static function _get_match($n)
  581. {
  582. $count = 0;
  583. foreach(F_Regexp::$_matches as $k=>$v)
  584. if(is_int($k))
  585. $count++;
  586. if($n < 0)
  587. $n += $count;
  588. if($n < 0)
  589. return new F_NilClass;
  590. if($n >= $count)
  591. return new F_NilClass;
  592. $val = F_Regexp::$_matches[$n];
  593. if(is_array($val))
  594. $val = $val[0];
  595. return F_String::__from_string($val);
  596. }
  597. public static function SF_escape($block, $str)
  598. {
  599. return F_String::__from_string(preg_quote($str->F_to_s(NULL)->__STRING));
  600. }
  601. public static function __from_string($str)
  602. {
  603. $r = new F_Regexp;
  604. $r->__REGEXP = $str;
  605. return $r;
  606. }
  607. public static function SF_new($block, $regexp, $options = NULL)
  608. {
  609. $opts = "";
  610. if($options !== NULL)
  611. $opts = $options->F_to_s(NULL)->__STRING;
  612. $r = new F_Regexp;
  613. $r->__REGEXP = '/' . str_replace('/', '\/', $regexp->F_to_s(NULL)->__STRING) . '/' . $opts;
  614. return $r;
  615. }
  616. public function __operator_eq($block, $operand)
  617. {
  618. return F_TrueClass::__from_bool(is_a($operand, 'F_Regexp') && $this->__REGEXP === $operand->__REGEXP);
  619. }
  620. public function __operator_stricteq($block, $operand)
  621. {
  622. return $this->__operator_match(NULL, $operand);
  623. }
  624. public function __operator_match($block, $operand)
  625. {
  626. if(preg_match($this->__REGEXP, $operand->F_to_s(NULL)->__STRING, F_Regexp::$_matches, PREG_OFFSET_CAPTURE) === 0)
  627. return new F_NilClass;
  628. return F_Number::__from_number(F_Regexp::$_matches[0][1]);
  629. }
  630. public function F_casefold_QUES_($block)
  631. {
  632. return F_TrueClass::__from_bool(preg_match('/\/[a-z]*i[a-z]*$/', $this->__REGEXP, F_Regexp::$_matches));
  633. }
  634. public function F_to_s($block)
  635. {
  636. return F_String::__from_string($this->__REGEXP);
  637. }
  638. public function F_source($block)
  639. {
  640. preg_match('/^\/(.*)\/[a-z]*$/', $this->__REGEXP, F_Regexp::$_matches);
  641. return F_String::__from_string(F_Regexp::$_matches[0]);
  642. }
  643. public function F_match($block, $str)
  644. {
  645. if(preg_match($this->__REGEXP, $str->F_to_s(NULL)->__STRING, F_Regexp::$_matches, PREG_OFFSET_CAPTURE) === 0)
  646. return new F_NilClass;
  647. return F_MatchData::__from_array(F_Regexp::$_matches, $this);
  648. }
  649. }
  650. class F_MatchData extends F_Object
  651. {
  652. public static function __from_array($matches, $regexp)
  653. {
  654. $md = new F_MatchData;
  655. $md->__MATCHES = $matches;
  656. $md->__REGEXP = $regexp;
  657. return $md;
  658. }
  659. public function __operator_arrayget($block, $idx)
  660. {
  661. $count = 0;
  662. foreach($this->__MATCHES as $k=>$v)
  663. if(is_int($k))
  664. $count++;
  665. if(is_a($idx, 'F_Number'))
  666. {
  667. $index = $idx->__NUMBER;
  668. if($index < 0)
  669. $index += $count;
  670. if($index < 0)
  671. return new F_NilClass;
  672. if($index >= $count)
  673. return new F_NilClass;
  674. return F_String::__from_string($this->__MATCHES[$index][0]);
  675. }
  676. $index = $idx->F_to_s(NULL)->__STRING;
  677. if(!isset($this->__MATCHES[$index]))
  678. return new F_NilClass;
  679. return F_String::__from_string($this->__MATCHES[$index][0]);
  680. }
  681. public function F_begin($block, $idx)
  682. {
  683. $count = 0;
  684. foreach($this->__MATCHES as $k=>$v)
  685. if(is_int($k))
  686. $count++;
  687. if(is_a($idx, 'F_Number'))
  688. {
  689. $index = $idx->__NUMBER;
  690. if($index < 0)
  691. $index += $count;
  692. if($index < 0)
  693. return new F_NilClass;
  694. if($index >= $count)
  695. return new F_NilClass;
  696. return F_Number::__from_number($this->__MATCHES[$index][1]);
  697. }
  698. $index = $idx->F_to_s(NULL)->__STRING;
  699. if(!isset($this->__MATCHES[$index]))
  700. return new F_NilClass;
  701. return F_Number::__from_number($this->__MATCHES[$index][1]);
  702. }
  703. public function F_captures($block)
  704. {
  705. $caps = array_map(create_function('$a','return F_String::__from_string($a[0]);'), $this->__MATCHES);
  706. array_shift($caps);
  707. return F_Array::__from_array($caps);
  708. }
  709. public function F_end($block, $idx)
  710. {
  711. $count = 0;
  712. foreach($this->__MATCHES as $k=>$v)
  713. if(is_int($k))
  714. $count++;
  715. if(is_a($idx, 'F_Number'))
  716. {
  717. $index = $idx->__NUMBER;
  718. if($index < 0)
  719. $index += $count;
  720. if($index < 0)
  721. return new F_NilClass;
  722. if($index >= $count)
  723. return new F_NilClass;
  724. return F_Number::__from_number($this->__MATCHES[$index][1] + strlen($this->__MATCHES[$index][0]));
  725. }
  726. $index = $idx->F_to_s(NULL)->__STRING;
  727. if(!isset($this->__MATCHES[$index]))
  728. return new F_NilClass;
  729. return F_Number::__from_number($this->__MATCHES[$index][1] + strlen($this->__MATCHES[$index][0]));
  730. }
  731. public function F_size($block)
  732. {
  733. $count = 0;
  734. foreach($this->__MATCHES as $k=>$v)
  735. if(is_int($k))
  736. $count++;
  737. return F_Number::__from_number($count);
  738. }
  739. public function F_regexp($block)
  740. {
  741. return $this->__REGEXP;
  742. }
  743. public function F_to_a($block)
  744. {
  745. return $this->F_captures(NULL);
  746. }
  747. public function F_to_s($block)
  748. {
  749. return F_String::__from_string($this->__MATCHES[0][0]);
  750. }
  751. }
  752. class F_Time extends F_Object
  753. {
  754. public static function SF_new($block)
  755. {
  756. $t = new F_Time;
  757. $t->__TIME = time();
  758. return $t;
  759. }
  760. public static function SF_now($block)
  761. {
  762. return F_Time::SF_new(NULL);
  763. }
  764. public static function SF_strtotime($block, $string)
  765. {
  766. $t = new F_Time;
  767. $t->__TIME = strtotime($string->F_to_s(NULL)->__STRING);
  768. return $t;
  769. }
  770. public function F_inspect($block)
  771. {
  772. return F_String::__from_string(strftime('%Y-%m-%d %H:%M:%S %z', $this->__TIME));
  773. }
  774. public function F_strftime($block, $string)
  775. {
  776. return F_String::__from_string(strftime($string->F_to_s(NULL)->__STRING, $this->__TIME));
  777. }
  778. public function F_to_s($block)
  779. {
  780. return $this->F_inspect(NULL);
  781. }
  782. }
  783. class F_Enumerable extends F_Object
  784. {
  785. public static $_states = array();
  786. public function F_all_QUES_($block)
  787. {
  788. if($block === NULL)
  789. $block = create_function('','$a = func_get_args(); return $a[1];');
  790. $state = count(F_Enumerable::$_states);
  791. F_Enumerable::$_states[$state] = true;
  792. $this->F_each(create_function('',sprintf('$a = func_get_args(); $f = "%s"; if(! _isTruthy($f(NULL, $a[1]))) { F_Enumerable::$_states[%d] = false; }', $block, $state)));
  793. return F_TrueClass::__from_bool(F_Enumerable::$_states[$state]);
  794. }
  795. public function F_any_QUES_($block)
  796. {
  797. if($block === NULL)
  798. $block = create_function('','$a = func_get_args(); return $a[1];');
  799. $state = count(F_Enumerable::$_states);
  800. F_Enumerable::$_states[$state] = false;
  801. $this->F_each(create_function('',sprintf('$a = func_get_args(); $f = "%s"; if(_isTruthy($f(NULL, $a[1]))) { F_Enumerable::$_states[%d] = true; }', $block, $state)));
  802. return F_TrueClass::__from_bool(F_Enumerable::$_states[$state]);
  803. }
  804. public function F_collect($block)
  805. {
  806. $state = count(F_Enumerable::$_states);
  807. F_Enumerable::$_states[$state] = array();
  808. $this->F_each(create_function('',sprintf('$a = func_get_args(); $f = "%s"; F_Enumerable::$_states[%d][] = $f(NULL, $a[1]);', $block, $state)));
  809. return F_Array::__from_array(F_Enumerable::$_states[$state]);
  810. }
  811. public function F_each_with_index($block)
  812. {
  813. $state = count(F_Enumerable::$_states);
  814. F_Enumerable::$_states[$state] = F_Number::__from_number(0);
  815. $this->F_each(create_function('',sprintf('$a = func_get_args(); $f = "%s"; $f(NULL, $a[1], F_Enumerable::$_states[%d]); F_Enumerable::$_states[%d] = F_Enumerable::$_states[%d]->F_succ(NULL);', $block, $state)));
  816. return new F_NilClass;
  817. }
  818. public function F_count($block, $item = NULL)
  819. {
  820. if($block === NULL && $item === NULL)
  821. {
  822. if(_isTruthy($this->F_respond_to_QUES_(NULL, F_Symbol::__from_string('size'))))
  823. return $this->F_size(NULL);
  824. $state = count(F_Enumerable::$_states);
  825. F_Enumerable::$_states[$state] = 0;
  826. $this->F_each(create_function('',sprintf('F_Enumerable::$_states[%d]++;', $state)));
  827. return F_Number::__from_number(F_Enumerable::$_states[$state]);
  828. }
  829. if($item !== NULL)
  830. {
  831. $state = count(F_Enumerable::$_states);
  832. F_Enumerable::$_states[$state] = array('item' => $item, 'count' => 0);
  833. $this->F_each(create_function('',sprintf('$a = func_get_args(); if(_isTruthy(F_Enumerable::$_states[%d]["item"]->__operator_eq(NULL, $a[1]))) { F_Enumerable::$_states[%d]["count"]++; }', $state, $state)));
  834. return F_Number::__from_number(F_Enumerable::$_states[$state]['count']);
  835. }
  836. $state = count(F_Enumerable::$_states);
  837. F_Enumerable::$_states[$state] = 0;
  838. $this->F_each(create_function('',sprintf('$a = func_get_args(); $f = "%s"; if(_isTruthy($f(NULL, $a[1]))) { F_Enumerable::$_states[%d]++; }', $block, $state)));
  839. return F_Number::__from_number(F_Enumerable::$_states[$state]);
  840. }
  841. public function F_find($block)
  842. {
  843. try
  844. {
  845. $this->F_each(create_function('',sprintf('$a = func_get_args(); $f = "%s"; if(_isTruthy($f(NULL, $a[1]))) { throw new ReturnFromBlock($a[1]); }', $block)));
  846. }
  847. catch(ReturnFromBlock $rfb)
  848. {
  849. return $rfb->val;
  850. }
  851. return new F_NilClass;
  852. }
  853. public function F_drop($block, $n)
  854. {
  855. $state = count(F_Enumerable::$_states);
  856. F_Enumerable::$_states[$state] = array('n' => $n->__NUMBER, 'arr' => array());
  857. $this->F_each(create_function('',sprintf('$a = func_get_args(); $state = %d;
  858. if(--F_Enumerable::$_states[$state]["n"] < 0)
  859. {
  860. F_Enumerable::$_states[$state]["arr"][] = $a[1];
  861. }', $state)));
  862. return F_Array::__from_array(F_Enumerable::$_states[$state]['arr']);
  863. }
  864. public function F_drop_while($block)
  865. {
  866. $state = count(F_Enumerable::$_states);
  867. F_Enumerable::$_states[$state] = array('dropping' => true, 'arr' => array());
  868. $this->F_each(create_function('',sprintf('$a = func_get_args(); $f = "%s";
  869. if(!F_Enumerable::$_states[%d]["dropping"] || !_isTruthy($f(NULL,$a[1]))) {
  870. F_Enumerable::$_states[%d]["dropping"] = false;
  871. F_Enumerable::$_states[%d]["arr"][] = $a[1];
  872. }', $block, $state, $state, $state)));
  873. return F_Array::__from_array(F_Enumerable::$_states[$state]['arr']);
  874. }
  875. public function F_to_a($block)
  876. {
  877. $state = count(F_Enumerable::$_states);
  878. F_Enumerable::$_states[$state] = array();
  879. $this->F_each(create_function('',sprintf('$a = func_get_args(); F_Enumerable::$_states[%d][] = $a[1];', $state)));
  880. return F_Array::__from_array(F_Enumerable::$_states[$state]);
  881. }
  882. public function F_select($block)
  883. {
  884. $state = count(F_Enumerable::$_states);
  885. F_Enumerable::$_states[$state] = array();
  886. $this->F_each(create_function('',sprintf('$a = func_get_args(); $f = "%s"; if(_isTruthy($f(NULL, $a[1]))) { F_Enumerable::$_states[%d][] = $a[1]; }', $block, $state)));
  887. return F_Array::__from_array(F_Enumerable::$_states[$state]);
  888. }
  889. public function F_first($block)
  890. {
  891. try
  892. {
  893. $this->F_each(create_function('','$a = func_get_args(); throw new ReturnFromBlock($a[1]);'));
  894. }
  895. catch(ReturnFromBlock $rfb)
  896. {
  897. return $rfb->val;
  898. }
  899. return new F_NilClass;
  900. }
  901. public function F_include_QUES_($block, $obj)
  902. {
  903. $state = count(F_Enumerable::$_states);
  904. F_Enumerable::$_states[$state] = $obj;
  905. try
  906. {
  907. $this->F_each(create_function('',sprintf('$a = func_get_args(); if(_isTruthy($a[1]->__operator_eq(NULL, F_Enumerable::$_states[%d]))) { throw new ReturnFromBlock($a[1]); }', $state, $obj)));
  908. }
  909. catch(ReturnFromBlock $rfb)
  910. {
  911. return $rfb->val;
  912. }
  913. return new F_NilClass;
  914. }
  915. public function F_reduce($block, $sym = NULL)
  916. {
  917. $state = count(F_Enumerable::$_states);
  918. F_Enumerable::$_states[$state] = NULL;
  919. if($block === NULL && $sym !== NULL)
  920. $block = create_function('',sprintf('$a = func_get_args(); return call_user_func(array($a[1],"%s"), NULL, $a[2]);', _rmethod_to_php($sym->__SYMBOL)));
  921. elseif($block === NULL)
  922. {
  923. // @TODO
  924. // throw some exception
  925. }
  926. elseif($sym !== NULL)
  927. F_Enumerable::$_states[$state] = $sym;
  928. $this->F_each(create_function('',sprintf('$a = func_get_args(); $state = %d; $f = "%s";
  929. if(F_Enumerable::$_states[$state] === NULL)
  930. {
  931. F_Enumerable::$_states[$state] = $a[1];
  932. }
  933. else
  934. {
  935. F_Enumerable::$_states[$state] = $f(NULL, F_Enumerable::$_states[$state], $a[1]);
  936. }', $state, $block)));
  937. return F_Enumerable::$_states[$state] === NULL ? new F_NilClass : F_Enumerable::$_states[$state];
  938. }
  939. public function _F_minmax($block, $compare)
  940. {
  941. if($block === NULL)
  942. $block = create_function('', '$a = func_get_args(); return $a[1]->__operator_spaceship(NULL, $a[2]);');
  943. $state = count(F_Enumerable::$_states);
  944. F_Enumerable::$_states[$state] = NULL;
  945. $this->F_each(create_function('',sprintf('$a = func_get_args(); $state = %d; $f = "%s";
  946. if(F_Enumerable::$_states[$state] === NULL)
  947. {
  948. F_Enumerable::$_states[$state] = $a[1];
  949. }
  950. else
  951. {
  952. if($f(NULL, F_Enumerable::$_states[$state], $a[1])->__NUMBER ' . $compare . ' 0)
  953. {
  954. F_Enumerable::$_states[$state] = $a[1];
  955. }
  956. }', $state, $block)));
  957. return F_Enumerable::$_states[$state] === NULL ? new F_NilClass : F_Enumerable::$_states[$state];
  958. }
  959. public function F_max($block)
  960. {
  961. return $this->_F_minmax($block, '<');
  962. }
  963. public function F_min($block)
  964. {
  965. return $this->_F_minmax($block, '>');
  966. }
  967. public function F_minmax($block)
  968. {
  969. return F_Array::__from_array(array($this->F_min($block), $this->F_max($block)));
  970. }
  971. public function F_none_QUES_($block)
  972. {
  973. return $this->F_any_QUES_($block) ? new F_FalseClass : new F_TrueClass;
  974. }
  975. public function F_one_QUES_($block)
  976. {
  977. if($block === NULL)
  978. $block = create_function('','$a = func_get_args(); return $a[1];');
  979. $state = count(F_Enumerable::$_states);
  980. F_Enumerable::$_states[$state] = 0;
  981. $this->F_each(create_function('',sprintf('$a = func_get_args(); $f = "%s"; if(_isTruthy($f(NULL, $a[1]))) { F_Enumerable::$_states[%d]++; }', $block, $state)));
  982. return F_TrueClass::__from_bool(F_Enumerable::$_states[$state] === 1);
  983. }
  984. public function F_partition($block)
  985. {
  986. $state = count(F_Enumerable::$_states);
  987. F_Enumerable::$_states[$state] = array('t' => array(), 'f' => array());
  988. $this->F_each(create_function('',sprintf('$a = func_get_args(); $f = "%s"; F_Enumerable::$_states[%d][ _isTruthy($f(NULL, $a[1])) ? "t" : "f" ][] = $a[1];', $block, $state)));
  989. return F_Array::__from_array(F_Enumerable::$_states[$state]);
  990. }
  991. public function F_reject($block)
  992. {
  993. $state = count(F_Enumerable::$_states);
  994. F_Enumerable::$_states[$state] = array();
  995. $this->F_each(create_function('',sprintf('$a = func_get_args(); $f = "%s"; if(!_isTruthy($f(NULL, $a[1]))) { F_Enumerable::$_states[%d][] = $a[1]; }', $block, $state)));
  996. return F_Array::__from_array(F_Enumerable::$_states[$state]);
  997. }
  998. public function F_reverse_each($block)
  999. {
  1000. $state = count(F_Enumerable::$_states);
  1001. F_Enumerable::$_states[$state] = array();
  1002. $this->F_each(create_function('',sprintf('$a = func_get_args(); array_unshift(F_Enumerable::$_states[%d], $a[1]);', $state)));
  1003. return F_Array::__from_array(F_Enumerable::$_states[$state])->F_each($block);
  1004. }
  1005. public function F_sort($block)
  1006. {
  1007. if($block === NULL)
  1008. $block = create_function('', '$a = func_get_args(); return $a[1]->__operator_spaceship(NULL, $a[2]);');
  1009. $a = $this->F_to_a(NULL);
  1010. usort($a->__ARRAY, create_function('$a,$b', sprintf('$f = "%s"; return $f(NULL, $a, $b)->__NUMBER;', $block)));
  1011. return $a;
  1012. }
  1013. public function F_take($block, $n)
  1014. {
  1015. $state = count(F_Enumerable::$_states);
  1016. F_Enumerable::$_states[$state] = array('n' => $n->__NUMBER, 'arr' => array());
  1017. try
  1018. {
  1019. $this->F_each(create_function('',sprintf('$a = func_get_args(); $state = %d; if(--F_Enumerable::$_states[$state]["n"] < 0)
  1020. {
  1021. throw new ReturnFromBlock(NULL);
  1022. }
  1023. else
  1024. {
  1025. F_Enumerable::$_states[$state]["arr"][] = $a[1];
  1026. }', $state)));
  1027. }
  1028. catch(ReturnFromBlock $rfb)
  1029. { }
  1030. return F_Array::__from_array(F_Enumerable::$_states[$state]['arr']);
  1031. }
  1032. public function F_take_while($block)
  1033. {
  1034. $state = count(F_Enumerable::$_states);
  1035. F_Enumerable::$_states[$state] = array();
  1036. try
  1037. {
  1038. $this->F_each(create_function('',sprintf('$a = func_get_args(); $f = "%s";
  1039. if(!_isTruthy($f(NULL,$a[1]))) {
  1040. throw new ReturnFromBlock(NULL);
  1041. }
  1042. F_Enumerable::$_states[%d][] = $a[1];
  1043. ', $block, $state)));
  1044. }
  1045. catch(ReturnFromBlock $rfb)
  1046. { }
  1047. return F_Array::__from_array(F_Enumerable::$_states[$state]);
  1048. }
  1049. public function F_zip($block)
  1050. {
  1051. $args = func_get_args();
  1052. array_shift($args);
  1053. $state = count(F_Enumerable::$_states);
  1054. F_Enumerable::$_states[$state] = array('idx' => F_Number::__from_number(0), 'arr' => array(), 'collections' => $args);
  1055. $this->F_each(create_function('$b,$item', sprintf('
  1056. $state = %d;
  1057. $arr = array($item);
  1058. for($i = 0; $i < count(F_Enumerable::$_states[$state]["collections"]); $i++)
  1059. {
  1060. $arr[] = F_Enumerable::$_states[$state]["collections"][$i]->__operator_arrayget(NULL, F_Enumerable::$_states[$state]["idx"]);
  1061. }
  1062. F_Enumerable::$_states[$state]["idx"] = F_Enumerable::$_states[$state]["idx"]->F_succ(NULL);
  1063. F_Enumerable::$_states[$state]["arr"][] = F_Array::__from_array($arr);
  1064. return new F_NilClass;
  1065. ', $state)));
  1066. return F_Array::__from_array(F_Enumerable::$_states[$state]['arr']);
  1067. }
  1068. }
  1069. class F_Array extends F_Enumerable
  1070. {
  1071. public function toPHP()
  1072. {
  1073. $arr = array();
  1074. foreach($this->__ARRAY as $v)
  1075. {
  1076. if(method_exists($v, "toPHP"))
  1077. $arr[] = $v->toPHP();
  1078. else
  1079. $arr[] = NULL;
  1080. }
  1081. return $arr;
  1082. }
  1083. public static function __from_array($arr)
  1084. {
  1085. $a = new F_Array;
  1086. $a->__ARRAY = $arr;
  1087. return $a;
  1088. }
  1089. public static function S__operator_arrayget($block)
  1090. {
  1091. $a = func_get_args();
  1092. array_shift($a); // remove $block
  1093. return F_Array::__from_array($a);
  1094. }
  1095. public function __operator_bitwiseand($block, $array)
  1096. {
  1097. // @TODO
  1098. // this is a O(n^2) intersection for now. i'll optimize it later
  1099. $intersect = array();
  1100. for($i = 0; $i < count($this->__ARRAY); $i++)
  1101. for($j = 0; $j < count($array->__ARRAY); $j++)
  1102. if(_isTruthy($this->__ARRAY[$i]->__operator_eq($array->__ARRAY[$j])))
  1103. $intersect[] = $this->__ARRAY[$i];
  1104. // @TODO
  1105. // ruby-doc says this operator returns the intersection without dupes.
  1106. // for now i'll just return dupes
  1107. return F_Array::__from_array($intersect);
  1108. }
  1109. public function __operator_mul($block, $operand)
  1110. {
  1111. if($index->F_class()->__SYMBOL === 'F_String')
  1112. return $this->F_join(NULL, $operand);
  1113. $arr = array();
  1114. for($i = 0; $i < $operand->__NUMBER; $i++)
  1115. $arr = array_merge($arr, $this->__ARRAY);
  1116. return F_Array::__from_array($arr);
  1117. }
  1118. public function __operator_add($block, $operand)
  1119. {
  1120. return F_Array::__from_array(array_merge($this->__ARRAY, $operand->__ARRAY));
  1121. }
  1122. public function __operator_sub($block, $operand)
  1123. {
  1124. $new = array();
  1125. for($i = 0; $i < count($this->__ARRAY); $i++)
  1126. if(!_isTruthy($operand->F_include_QUES_(NULL, $this->__ARRAY[$i])))
  1127. $new[] = $this->__ARRAY[$i];
  1128. return F_Array::__from_array($new);
  1129. }
  1130. public function __operator_lshift($block, $operand)
  1131. {
  1132. $this->__ARRAY[] = $operand;
  1133. return $this;
  1134. }
  1135. public function __operator_eq($block, $operand)
  1136. {
  1137. if(count($this->__ARRAY) !== count($operand->__ARRAY))
  1138. return new F_FalseClass;
  1139. for($i = 0; $i < count($this->__ARRAY); $i++)
  1140. if(!_isTruthy($this->__ARRAY[$i]->__operator_eq(NULL, $operand->__ARRAY[$i])))
  1141. return new F_FalseClass;
  1142. return new F_TrueClass;
  1143. }
  1144. public function __operator_stricteq($block, $operand)
  1145. {
  1146. return $this->F_include_QUES_(NULL, $operand);
  1147. }
  1148. public function __operator_arrayget($block, $index)
  1149. {
  1150. $idx = (int)$index->__NUMBER;
  1151. if($idx < 0)
  1152. $idx += count($this->__ARRAY);
  1153. if($idx < 0)
  1154. {
  1155. // @TODO throw IndexError
  1156. return;
  1157. }
  1158. if($idx >= count($this->__ARRAY))
  1159. return new F_NilClass;
  1160. return $this->__ARRAY[$idx];
  1161. }
  1162. public function __operator_arrayset($block, $index, $val)
  1163. {
  1164. $idx = (int)$index->__NUMBER;
  1165. if($idx < 0)
  1166. $idx += count($this->__ARRAY);
  1167. if($idx < 0)
  1168. {
  1169. // @TODO throw IndexError
  1170. return;
  1171. }
  1172. if($idx >= count($this->__ARRAY))
  1173. {
  1174. for($i = count($this->__ARRAY); $i < $idx; $i++)
  1175. $this->__ARRAY[$i] = new F_NilClass;
  1176. }
  1177. $this->__ARRAY[$idx] = $val;
  1178. return $val;
  1179. }
  1180. public function F_clear($block)
  1181. {
  1182. $this->__ARRAY = array();
  1183. return $this;
  1184. }
  1185. public function F_join($block, $sep = NULL)
  1186. {
  1187. $seper = "";
  1188. if($sep !== NULL)
  1189. $seper = $sep->F_to_s(NULL)->__STRING;
  1190. return F_String::__from_string(implode($seper, array_map(create_function('$el','return $el->F_to_s(NULL)->__STRING;'), $this->__ARRAY)));
  1191. }
  1192. public function F_compact($block)
  1193. {
  1194. $new = array();
  1195. for($i = 0; $i < count($this->__ARRAY); $i++)
  1196. if(get_class($this->__ARRAY[$i]) !== 'F_NilClass')
  1197. $new[] = $this->__ARRAY[$i];
  1198. return F_Array::__from_array($new);
  1199. }
  1200. public function F_compact_EXCL($block)
  1201. {
  1202. $new = array();
  1203. $changed = false;
  1204. for($i = 0; $i < count($this->__ARRAY); $i++)
  1205. if(get_class($this->__ARRAY[$i]) !== 'F_NilClass')
  1206. {
  1207. $changed = true;
  1208. $new[] = $this->__ARRAY[$i];
  1209. }
  1210. $this->__ARRAY = $new;
  1211. if(!$changed)
  1212. return new F_NilClass;
  1213. return $this;
  1214. }
  1215. public function F_concat($block, $ary)
  1216. {
  1217. return $this->__operator_add(NULL, $ary);
  1218. }
  1219. public function F_delete($block, $val)
  1220. {
  1221. $new = array();
  1222. $changed = false;
  1223. for($i = 0; $i < count($this->__ARRAY); $i++)
  1224. if(!_isTruthy($this->__ARRAY[$i]->__operator_eq(NULL, $val)))
  1225. {
  1226. $changed = true;
  1227. $new[] = $this->__ARRAY[$i];
  1228. }
  1229. if($changed)
  1230. return $val;
  1231. else
  1232. {
  1233. if($block !== NULL)
  1234. return $block(NULL);
  1235. return new F_NilClass;
  1236. }
  1237. }
  1238. public function F_delete_at($block, $index)
  1239. {
  1240. $idx = (int)$index->__NUMBER;
  1241. if($idx < 0)
  1242. $idx += count($this->__ARRAY);
  1243. if($idx < 0)
  1244. return new F_NilClass;
  1245. $val = $this->__operator_arrayget(NULL, $index);
  1246. array_splice($this->__ARRAY, $idx, 1);
  1247. return $val;
  1248. }
  1249. public function F_delete_if($block)
  1250. {
  1251. $new = array();
  1252. for($i = 0; $i < count($this->__ARRAY); $i++)
  1253. if(!_isTruthy($block(NULL, $this->__ARRAY[$i])))
  1254. $new[] = $this->__ARRAY[$i];
  1255. $this->__ARRAY = $new;
  1256. return $this;
  1257. }
  1258. public function F_empty_QUES_($block)
  1259. {
  1260. return F_TrueClass::__from_bool(count($this->__ARRAY) === 0);
  1261. }
  1262. public function F_include_QUES_($block, $val)
  1263. {
  1264. for($i = 0; $i < count($this->__ARRAY); $i++)
  1265. if(_isTruthy($this->__ARRAY[$i]->__operator_eq(NULL, $val)))
  1266. return new F_TrueClass;
  1267. return new F_FalseClass;
  1268. }
  1269. public function F_to_s($block)
  1270. {
  1271. return F_String::__from_string('[ ' . implode(', ', array_map(create_function('$o','return $o->F_inspect(NULL)->__STRING;'), $this->__ARRAY)) . ' ]');
  1272. }
  1273. public function F_index($block, $val)
  1274. {
  1275. for($i = 0; $i < count($this->__ARRAY); $i++)
  1276. if(_isTruthy($this->__ARRAY[$i]->__operator_eq(NULL, $val)))
  1277. return F_Number::__from_number($i);
  1278. return new F_NilClass;
  1279. }
  1280. public function F_push($block, $val)
  1281. {
  1282. $this->__ARRAY[] = $val;
  1283. return $this;
  1284. }
  1285. public function F_replace($block, $ary)
  1286. {
  1287. $this->__ARRAY = $ary->__ARRAY;
  1288. return $this;
  1289. }
  1290. public function F_insert($block, $index)
  1291. {
  1292. $objs = func_get_args();
  1293. array_splice($objs, 0, 2);
  1294. $idx = (int)$index->__NUMBER;
  1295. if($idx < 0)
  1296. $idx += count($this->__ARRAY);
  1297. if($idx < 0)
  1298. {
  1299. // @TODO throw IndexError
  1300. return;
  1301. }
  1302. }
  1303. public function F_each($block)
  1304. {
  1305. if($block !== NULL)
  1306. {
  1307. foreach($this->__ARRAY as $i)
  1308. $block(NULL, $i);
  1309. return new F_NilClass;
  1310. }
  1311. return F_Enumerator::__from_array($this->__ARRAY);
  1312. }
  1313. }
  1314. class F_Range extends F_Enumerable
  1315. {
  1316. public function toPHP()
  1317. {
  1318. $arr = array();
  1319. $this->enumerate_range();
  1320. foreach($this->__RANGE as $v)
  1321. {
  1322. if(method_exists($v, "toPHP"))
  1323. $arr[] = $v->toPHP();
  1324. else
  1325. $arr[] = NULL;
  1326. }
  1327. return $arr;
  1328. }
  1329. public static function SF_new($block, $begin, $end, $exclusive = NULL)
  1330. {
  1331. $r = new F_Range;
  1332. $r->__BEGIN = $begin;
  1333. $r->__END = $end;
  1334. $r->__EXCLUSIVE = $exclusive === NULL ? FALSE : _isTruthy($exclusive);
  1335. return $r;
  1336. }
  1337. private function enumerate_range()
  1338. {
  1339. if(isset($this->__RANGE))
  1340. return;
  1341. $i = $this->__BEGIN;
  1342. while(true)
  1343. {
  1344. if(_isTruthy($this->__END->__operator_eq(NULL, $i)))
  1345. {
  1346. if(!$this->__EXCLUSIVE)
  1347. $this->__RANGE[] = $i;
  1348. break;
  1349. }
  1350. $this->__RANGE[] = $i;
  1351. if(!_isTruthy($i->F_respond_to_QUES_(NULL, F_Symbol::__from_string('succ'))))
  1352. {
  1353. throw new ErrorCarrier(F_Error::SF_new(NULL, F_String::__from_string('Item in range does not respond to #succ')));
  1354. }
  1355. $i = $i->F_succ(NULL);
  1356. }
  1357. }
  1358. public function __operator_eq($block, $operand)
  1359. {
  1360. return F_TrueClass::__from_bool(_isTruthy($this->__BEGIN->__operator_eq(NULL, $operand->__BEGIN))
  1361. && _isTruthy($this->__END->__operator_eq(NULL, $operand->__END))
  1362. && $this->__EXCLUSIVE === $operand->__EXCLUSIVE);
  1363. }
  1364. public function __operator_stricteq($block, $operand)
  1365. {
  1366. if(_isTruthy($operand->F_respond_to_QUES_(NULL, F_Symbol::__from_string("<=>"))))
  1367. {
  1368. if($operand->__operator_spaceship(NULL, $this->__BEGIN)->__NUMBER >= 0
  1369. && $operand->__operator_spaceship(NULL, $this->__END)->__NUMBER <= 0)
  1370. {
  1371. return new F_TrueClass;
  1372. }
  1373. else
  1374. {
  1375. return new F_FalseClass;
  1376. }
  1377. }
  1378. // we're going to have to enumerate the range to see if $operand included
  1379. $this->enumerate_range();
  1380. foreach($this->__RANGE as $i)
  1381. if(_isTruthy($i->__operator_eq(NULL, $operand)))
  1382. return new F_TrueClass;
  1383. return new F_FalseClass;
  1384. }
  1385. public function F_begin($block)
  1386. {
  1387. return $this->__BEGIN;
  1388. }
  1389. public function F_cover_QUES_($block, $val)
  1390. {
  1391. return $this->__operator_stricteq(NULL, $val);
  1392. }
  1393. public function F_include_QUES_($block, $val)
  1394. {
  1395. return $this->__operator_stricteq(NULL, $val);
  1396. }
  1397. public function F_each($block)
  1398. {
  1399. $this->enumerate_range();
  1400. if($block === NULL)
  1401. return F_Enumerator::__from_array($this->__RANGE);
  1402. foreach($this->__RANGE as $i)
  1403. $block(NULL, $i);
  1404. }
  1405. public function F_end($block)
  1406. {
  1407. return $this->__END;
  1408. }
  1409. public function F_exclude_end_QUES_($block)
  1410. {
  1411. return F_TrueClass::__from_bool($this->__EXCLUSIVE);
  1412. }
  1413. public function F_to_a($block)
  1414. {
  1415. $this->enumerate_range();
  1416. return F_Array::__from_array($this->__RANGE);
  1417. }
  1418. }
  1419. class F_Hash extends F_Enumerable
  1420. {
  1421. public function toPHP()
  1422. {
  1423. $arr = array();
  1424. foreach($this->__PAIRS as $kv)
  1425. {
  1426. $k = $kv->__ARRAY[0]->F_to_s(NULL)->__STRING;
  1427. $v = $kv->__ARRAY[1];
  1428. if(method_exists($v, "toPHP"))
  1429. $arr[$k] = $v->toPHP();
  1430. else
  1431. $arr[$k] = NULL;
  1432. }
  1433. return $arr;
  1434. }
  1435. public static function __from_pairs($pairs)
  1436. {
  1437. $hash = new F_Hash;
  1438. $hash->__DEFAULT = new F_NilClass;
  1439. $hash->__PAIRS = $pairs;
  1440. return $hash;
  1441. }
  1442. public static function __from_flatpairs($flatpairs)
  1443. {
  1444. $hash = new F_Hash;
  1445. $hash->__DEFAULT = new F_NilClass;
  1446. $hash->__PAIRS = array();
  1447. for($i = 0; $i < count($flatpairs); $i += 2)
  1448. $hash->__PAIRS[] = F_Array::__from_array(array($flatpairs[$i], $flatpairs[$i+1]));
  1449. return $hash;
  1450. }
  1451. public static function __from_assoc($assoc)
  1452. {
  1453. $hash = new F_Hash;
  1454. $hash->__DEFAULT = new F_NilClass;
  1455. foreach($assoc as $k => $v)
  1456. $hash->__PAIRS[] = F_Array::__from_array(array(F_Symbol::__from_string($k), F_String::__from_string($v)));
  1457. return $hash;
  1458. }
  1459. public static function F_insert($block, $arr)
  1460. {
  1461. return $this->__operator_arrayset(NULL, $arr->__operator_arrayget(NULL, F_Number::__from_number(0)), $arr->__operator_arrayget(NULL, F_Number::__from_number(1)));
  1462. }
  1463. public static function SF_new($block, $obj = NULL)
  1464. {
  1465. $hsh = new F_Hash;
  1466. if($block === NULL && $obj === NULL)
  1467. {
  1468. $hsh->__DEFAULT = new F_NilClass;
  1469. }
  1470. elseif($obj !== NULL)
  1471. {
  1472. $hsh->__DEFAULT = $obj;
  1473. }
  1474. else
  1475. {
  1476. $hsh->__DEFAULT = $block;
  1477. }
  1478. $hsh->__PAIRS = array();
  1479. return $hsh;
  1480. }
  1481. public function __operator_arrayget($block, $key)
  1482. {
  1483. foreach($this->__PAIRS as $pair)
  1484. {
  1485. if(_isTruthy($pair->__ARRAY[0]->__operator_eq(NULL, $key)))
  1486. return $pair->__ARRAY[1];
  1487. }
  1488. return $this->F_default(NULL, $key);
  1489. }
  1490. public function __operator_arrayset($block, $key, $val)
  1491. {
  1492. foreach($this->__PAIRS as $pair)
  1493. {
  1494. if(_isTruthy($pair->__ARRAY[0]->__operator_eq(NULL, $key)))
  1495. {
  1496. $pair->__ARRAY[1] = $val;
  1497. return $pair->__ARRAY[0];
  1498. }
  1499. }
  1500. $this->__PAIRS[] = F_Array::__from_array(array($key->F_dup(NULL), $val));
  1501. return $val;
  1502. }
  1503. public function F_assoc($block, $key)
  1504. {
  1505. foreach($this->__PAIRS as $pair)
  1506. {
  1507. if(_isTruthy($pair->__ARRAY[0]->__operator_eq(NULL, $key)))
  1508. return $pair->__ARRAY[1];
  1509. }
  1510. return new F_NilClass;
  1511. }
  1512. public function F_clear($block)
  1513. {
  1514. $this->__PAIRS = array();
  1515. }
  1516. public function F_default($block, $key = NULL)
  1517. {
  1518. if(is_string($this->__DEFAULT) && $key !== NULL)
  1519. {
  1520. $blockfn = $this->__DEFAULT;
  1521. return $blockfn(NULL, $this, $key);
  1522. }
  1523. return $this->__DEFAULT;
  1524. }
  1525. public function F_default__set($block, $default)
  1526. {
  1527. $this->__DEFAULT = $default;
  1528. }
  1529. public function F_delete($block, $key)
  1530. {
  1531. $new_pairs = array();
  1532. $val = NULL;
  1533. foreach($this->__PAIRS as $pair)
  1534. {
  1535. if(!_isTruthy($pair->__ARRAY[0]->__operator_eq(NULL, $key)))
  1536. $new_pairs[] = $pair;
  1537. else
  1538. $val = $pair->__ARRAY[1];
  1539. }
  1540. if($val === NULL)
  1541. {
  1542. if($block === NULL)
  1543. return new F_NilClass;
  1544. return $block(NULL, $key);
  1545. }
  1546. else
  1547. {
  1548. $this->__PAIRS = $new_pairs;
  1549. return $val;
  1550. }
  1551. }
  1552. public function F_delete_if($block)
  1553. {
  1554. $new_pairs = array();
  1555. $old_pairs = array();
  1556. foreach($this->__PAIRS as $pair)
  1557. {
  1558. if(!_isTruthy($block(NULL, $pair->__ARRAY[0], $pair->__ARRAY[1])))
  1559. $new_pairs[] = $pair;
  1560. else
  1561. $old_pairs[] = $pair;
  1562. }
  1563. $this->__PAIRS = $new_pairs;
  1564. return F_Hash::__from_pairs($old_pairs);
  1565. }
  1566. public function F_each($block)
  1567. {
  1568. if($block !== NULL)
  1569. {
  1570. foreach($this->__PAIRS as $pair)
  1571. $block(NULL, $pair->__ARRAY[0], $pair->__ARRAY[1]);
  1572. }
  1573. return F_Enumerator::__from_array($this->__PAIRS);
  1574. }
  1575. public function F_each_key($block)
  1576. {
  1577. foreach($this->__PAIRS as $pair)
  1578. $block(NULL, $pair->__ARRAY[0]);
  1579. }
  1580. public function F_each_value($block)
  1581. {
  1582. foreach($this->__PAIRS as $pair)
  1583. $block(NULL, $pair->__ARRAY[1]);
  1584. }
  1585. public function F_empty_QUES_($block)
  1586. {
  1587. return F_TrueClass::__from_bool(count($this->__PAIRS) === 0);
  1588. }
  1589. public function F_has_key_QUES_($block, $key)
  1590. {
  1591. foreach($this->__PAIRS as $pair)
  1592. if(_isTruthy($pair->__ARRAY[0]->__operator_eq(NULL, $key)))
  1593. return new F_TrueClass;
  1594. return new F_FalseClass;
  1595. }
  1596. public function F_has_value_QUES_($block, $val)
  1597. {
  1598. foreach($this->__PAIRS as $pair)
  1599. if(_isTruthy($pair->__ARRAY[1]->__operator_eq(NULL, $val)))
  1600. return new F_TrueClass;
  1601. return new F_FalseClass;
  1602. }
  1603. public function F_to_s($block)
  1604. {
  1605. $str = "{ ";
  1606. $first = TRUE;
  1607. foreach($this->__PAIRS as $pairs)
  1608. {
  1609. if(!$first)
  1610. $str .= ", ";
  1611. $first = FALSE;
  1612. $str .= $pairs->__ARRAY[0]->F_to_s(NULL)->__STRING . " => " . $pairs->__ARRAY[1]->F_to_s(NULL)->__STRING;
  1613. }
  1614. $str .= " }";
  1615. return F_String::__from_string($str);
  1616. }
  1617. public function F_inspect($block)
  1618. {
  1619. return $this->F_to_s(NULL);
  1620. }
  1621. public function F_invert($block)
  1622. {
  1623. $new_pairs = array();
  1624. foreach($this->__PAIRS as $pair)
  1625. $new_pairs[] = F_Array::__from_array(array($pair->__ARRAY[1], $pair->__ARRAY[0]));
  1626. return F_Hash::__from_pairs($new_pairs);
  1627. }
  1628. public function F_keep_if($block)
  1629. {
  1630. $new_pairs = array();
  1631. $old_pairs = array();
  1632. foreach($this->__PAIRS as $pair)
  1633. {
  1634. if(_isTruthy($block(NULL, $pair->__ARRAY[0], $pair->__ARRAY[1])))
  1635. $new_pairs[] = $pair;
  1636. else
  1637. $old_pairs[] = $pair;
  1638. }
  1639. $this->__PAIRS = $new_pairs;
  1640. return F_Hash::__from_pairs($old_pairs);
  1641. }
  1642. public function F_key($block, $val)
  1643. {
  1644. foreach($this->__PAIRS as $pair)
  1645. {
  1646. if(_isTruthy($pair->__ARRAY[1]->__operator_eq(NULL, $val)))
  1647. return $pair->__ARRAY[0];
  1648. }
  1649. return new F_NilClass;
  1650. }
  1651. public function F_keys($block)
  1652. {
  1653. $arr = array();
  1654. foreach($this->__PAIRS as $pair)
  1655. $arr[] = $pair->__ARRAY[0];
  1656. return F_Array::__from_array($arr);
  1657. }
  1658. public function F_size($block)
  1659. {
  1660. return F_Number::__from_number(count($this->__PAIRS));
  1661. }
  1662. public function F_merge($block, $other)
  1663. {
  1664. $new = F_Hash::SF_new(NULL);
  1665. foreach($this->__PAIRS as $pair)
  1666. $new->__operator_arrayset(NULL, $pair->__ARRAY[0], $pair->__ARRAY[1]);
  1667. foreach($other->__PAIRS as $pair)
  1668. $new->__operator_arrayset(NULL, $pair->__ARRAY[0], $pair->__ARRAY[1]);
  1669. return $new;
  1670. }
  1671. public function F_merge_EXCL_($block, $other)
  1672. {
  1673. foreach($other->__PAIRS as $pair)
  1674. $this->__operator_arrayset(NULL, $pair->__ARRAY[0], $pair->__ARRAY[1]);
  1675. return $this;
  1676. }
  1677. public function F_reject($block)
  1678. {
  1679. $new_pairs = array();
  1680. foreach($this->__PAIRS as $pair)
  1681. {
  1682. if(!_isTruthy($block(NULL, $pair->__ARRAY[0], $pair->__ARRAY[1])))
  1683. $new_pairs[] = $pair;
  1684. }
  1685. return F_Hash::__from_pairs($new_pairs);
  1686. }
  1687. public function F_shift($block)
  1688. {
  1689. return array_shift($this->__PAIRS);
  1690. }
  1691. public function F_to_hash($block)
  1692. {
  1693. return $this;
  1694. }
  1695. public function F_values($block)
  1696. {
  1697. $arr = array();
  1698. foreach($this->__PAIRS as $pair)
  1699. $arr[] = $pair->__ARRAY[1];
  1700. return F_Array::__from_array($arr);
  1701. }
  1702. }
  1703. class F_Number extends F_Object
  1704. {
  1705. public function toPHP()
  1706. {
  1707. return $this->__NUMBER;
  1708. }
  1709. public static function __from_number($num)
  1710. {
  1711. $obj = new F_Number;
  1712. $obj->__NUMBER = $num;
  1713. return $obj;
  1714. }
  1715. public function __operator_mod($block,$operand)
  1716. {
  1717. return F_Number::__from_number($this->__NUMBER % $operand->__NUMBER);
  1718. }
  1719. public function __operator_bitwiseand($block,$operand)
  1720. {
  1721. return F_Number::__from_number($this->__NUMBER & $operand->__NUMBER);
  1722. }
  1723. public function __operator_mul($block,$operand)
  1724. {
  1725. return F_Number::__from_number($this->__NUMBER * $operand->__NUMBER);
  1726. }

Large files files are truncated, but you can click here to view the full file