function _equal_Q($a, $b) { $ota = gettype($a) === "object" ? get_class($a) : gettype($a); $otb = gettype($b) === "object" ? get_class($b) : gettype($b); if (!($ota === $otb or _sequential_Q($a) and _sequential_Q($b))) { return false; } elseif (_symbol_Q($a)) { #print "ota: $ota, otb: $otb\n"; return $a->value === $b->value; } elseif (_list_Q($a) or _vector_Q($a)) { if ($a->count() !== $b->count()) { return false; } for ($i = 0; $i < $a->count(); $i++) { if (!_equal_Q($a[$i], $b[$i])) { return false; } } return true; } elseif (_hash_map_Q($a)) { if ($a->count() !== $b->count()) { return false; } $hm1 = $a->getArrayCopy(); $hm2 = $b->getArrayCopy(); foreach (array_keys($hm1) as $k) { if ($hm1[$k] !== $hm2[$k]) { return false; } } return true; } else { return $a === $b; } }
function _pr_str($obj, $print_readably = True) { if (_list_Q($obj)) { $ret = array(); foreach ($obj as $e) { array_push($ret, _pr_str($e, $print_readably)); } return "(" . implode(" ", $ret) . ")"; } elseif (_vector_Q($obj)) { $ret = array(); foreach ($obj as $e) { array_push($ret, _pr_str($e, $print_readably)); } return "[" . implode(" ", $ret) . "]"; } elseif (_hash_map_Q($obj)) { $ret = array(); foreach (array_keys($obj->getArrayCopy()) as $k) { $ret[] = _pr_str($k, $print_readably); $ret[] = _pr_str($obj[$k], $print_readably); } return "{" . implode(" ", $ret) . "}"; } elseif (is_string($obj)) { if (strpos($obj, chr(0x7f)) === 0) { return ":" . substr($obj, 1); } elseif ($print_readably) { $obj = preg_replace('/"/', '\\"', preg_replace('/\\\\/', '\\\\\\\\', $obj)); return '"' . $obj . '"'; } else { return $obj; } } elseif (is_integer($obj)) { return $obj; } elseif ($obj === NULL) { return "nil"; } elseif ($obj === true) { return "true"; } elseif ($obj === false) { return "false"; } elseif (_symbol_Q($obj)) { return $obj->value; } elseif (_atom_Q($obj)) { return "(atom " . _pr_str($obj->value, $print_readably) . ")"; } elseif (_function_Q($obj)) { return "(fn* [...] ...)"; } elseif (is_callable($obj)) { // only step4 and below return "#<function ...>"; } else { throw new Exception("_pr_str unknown type: " . gettype($obj)); } }
function eval_ast($ast, $env) { if (_symbol_Q($ast)) { return $env->get($ast); } elseif (_sequential_Q($ast)) { if (_list_Q($ast)) { $el = _list(); } else { $el = _vector(); } foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); } return $el; } elseif (_hash_map_Q($ast)) { $new_hm = _hash_map(); foreach (array_keys($ast->getArrayCopy()) as $key) { $new_hm[$key] = MAL_EVAL($ast[$key], $env); } return $new_hm; } else { return $ast; } }
}, '/' => function ($a, $b) { return intval($a / $b, 10); }, 'time-ms' => function () { return time_ms(); }, 'list' => function () { return call_user_func_array('_list', func_get_args()); }, 'list?' => function ($a) { return _list_Q($a); }, 'vector' => function () { return call_user_func_array('_vector', func_get_args()); }, 'vector?' => function ($a) { return _vector_Q($a); }, 'hash-map' => function () { return call_user_func_array('_hash_map', func_get_args()); }, 'map?' => function ($a) { return _hash_map_Q($a); }, 'assoc' => function () { return call_user_func_array('assoc', func_get_args()); }, 'dissoc' => function () { return call_user_func_array('dissoc', func_get_args()); }, 'get' => function ($a, $b) { return get($a, $b); }, 'contains?' => function ($a, $b) { return contains_Q($a, $b); }, 'keys' => function ($a) { return keys($a); }, 'vals' => function ($a) { return vals($a); }, 'sequential?' => function ($a) { return _sequential_Q($a); }, 'cons' => function ($a, $b) {