2020-05-28 10:10:00 +00:00
|
|
|
<?php
|
|
|
|
namespace MadLisp;
|
|
|
|
|
|
|
|
class Reader
|
|
|
|
{
|
2020-06-04 02:10:48 +00:00
|
|
|
public function read(array $tokens)
|
2020-05-28 10:10:00 +00:00
|
|
|
{
|
2020-06-04 11:27:47 +00:00
|
|
|
if (empty($tokens)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-05-28 10:10:00 +00:00
|
|
|
$index = 0;
|
2020-06-04 02:10:48 +00:00
|
|
|
return $this->readForm($tokens, $index);
|
2020-05-28 10:10:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private function readForm(array $tokens, int &$index)
|
|
|
|
{
|
2020-05-31 04:50:53 +00:00
|
|
|
if ($tokens[$index] == "'") {
|
2020-12-05 10:38:42 +00:00
|
|
|
return $this->readSpecialForm($tokens, $index, 'quote');
|
|
|
|
} elseif ($tokens[$index] == "`") {
|
|
|
|
return $this->readSpecialForm($tokens, $index, 'quasiquote');
|
|
|
|
} elseif ($tokens[$index] == "~") {
|
|
|
|
return $this->readSpecialForm($tokens, $index, 'unquote');
|
2020-05-31 04:50:53 +00:00
|
|
|
} elseif ($tokens[$index] == '(') {
|
2020-05-28 10:10:00 +00:00
|
|
|
return $this->readList($tokens, $index);
|
2020-05-31 04:34:24 +00:00
|
|
|
} elseif ($tokens[$index] == '[') {
|
|
|
|
return $this->readVector($tokens, $index);
|
|
|
|
} elseif ($tokens[$index] == '{') {
|
|
|
|
return $this->readHash($tokens, $index);
|
2020-05-28 10:10:00 +00:00
|
|
|
} else {
|
|
|
|
return $this->readAtom($tokens, $index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-05 10:38:42 +00:00
|
|
|
private function readSpecialForm(array $tokens, int &$index, string $symbol)
|
|
|
|
{
|
|
|
|
$index++;
|
|
|
|
$contents = [new Symbol($symbol)];
|
|
|
|
if ($index < count($tokens) && !in_array($tokens[$index], [')', ']', '}'])) {
|
|
|
|
$contents[] = $this->readForm($tokens, $index);
|
|
|
|
}
|
|
|
|
return new MList($contents);
|
|
|
|
}
|
|
|
|
|
2020-05-28 10:10:00 +00:00
|
|
|
private function readList(array $tokens, int &$index): MList
|
2020-05-31 04:34:24 +00:00
|
|
|
{
|
|
|
|
return new MList($this->readCollection($tokens, $index, ')'));
|
|
|
|
}
|
|
|
|
|
|
|
|
private function readVector(array $tokens, int &$index): Vector
|
|
|
|
{
|
|
|
|
return new Vector($this->readCollection($tokens, $index, ']'));
|
|
|
|
}
|
|
|
|
|
|
|
|
private function readHash(array $tokens, int &$index): Hash
|
|
|
|
{
|
|
|
|
$contents = $this->readCollection($tokens, $index, '}');
|
|
|
|
return Util::makeHash($contents);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function readCollection(array $tokens, int &$index, string $endTag): array
|
2020-05-28 10:10:00 +00:00
|
|
|
{
|
|
|
|
$result = [];
|
|
|
|
|
|
|
|
// start tag
|
|
|
|
$index++;
|
|
|
|
|
2020-05-31 04:34:24 +00:00
|
|
|
while ($tokens[$index] != $endTag) {
|
2020-05-28 10:10:00 +00:00
|
|
|
$result[] = $this->readForm($tokens, $index);
|
|
|
|
}
|
|
|
|
|
|
|
|
// end tag
|
|
|
|
$index++;
|
|
|
|
|
2020-05-31 04:34:24 +00:00
|
|
|
return $result;
|
2020-05-28 10:10:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private function readAtom(array $tokens, int &$index)
|
|
|
|
{
|
|
|
|
$a = $tokens[$index++];
|
|
|
|
|
|
|
|
if ($a === 'true') {
|
|
|
|
return true;
|
|
|
|
} elseif ($a === 'false') {
|
|
|
|
return false;
|
|
|
|
} elseif ($a === 'null') {
|
|
|
|
return null;
|
|
|
|
} elseif (substr($a, 0, 1) === '"') {
|
2020-06-16 13:15:57 +00:00
|
|
|
// string, handle special characters
|
|
|
|
$a = substr($a, 1, -1);
|
|
|
|
$a = str_replace("\\\\", chr(0x7f), $a);
|
|
|
|
$a = str_replace("\\n", "\n", $a);
|
|
|
|
$a = str_replace("\\r", "\r", $a);
|
|
|
|
$a = str_replace("\\\"", "\"", $a);
|
|
|
|
return str_replace(chr(0x7f), "\\", $a);
|
2020-05-28 10:10:00 +00:00
|
|
|
} elseif (is_numeric($a)) {
|
|
|
|
if (filter_var($a, FILTER_VALIDATE_INT) !== false) {
|
|
|
|
return intval($a);
|
|
|
|
} else {
|
|
|
|
return floatval($a);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return new Symbol($a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|