forked from kanaka/mal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
reader.php
117 lines (103 loc) · 3.42 KB
/
reader.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
<?php
require_once 'types.php';
class Reader {
protected $tokens = array();
protected $position = 0;
public function __construct($tokens) {
$this->tokens = $tokens;
$this->position = 0;
}
public function next() {
if ($this->position >= count($this->tokens)) { return null; }
return $this->tokens[$this->position++];
}
public function peek() {
if ($this->position >= count($this->tokens)) { return null; }
return $this->tokens[$this->position];
}
}
class BlankException extends Exception {
}
function _real_token($s) {
return $s !== '' && $s[0] !== ';';
}
function tokenize($str) {
$pat = "/[\s,]*(~@|[\[\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"|;.*|[^\s\[\]{}('\"`,;)]*)/";
preg_match_all($pat, $str, $matches);
return array_values(array_filter($matches[1], '_real_token'));
}
function read_atom($reader) {
$token = $reader->next();
if (preg_match("/^-?[0-9]+$/", $token)) {
return intval($token, 10);
} elseif ($token[0] === "\"") {
$str = substr($token, 1, -1);
$str = preg_replace('/\\\\"/', '"', $str);
return $str;
} elseif ($token === "nil") {
return NULL;
} elseif ($token === "true") {
return true;
} elseif ($token === "false") {
return false;
} else {
return _symbol($token);
}
}
function read_list($reader, $constr='_list', $start='(', $end=')') {
$ast = $constr();
$token = $reader->next();
if ($token !== $start) {
throw new Exception("expected '" . $start . "'");
}
while (($token = $reader->peek()) !== $end) {
if ($token === "" || $token === null) {
throw new Exception("expected '" . $end . "', got EOF");
}
$ast[] = read_form($reader);
}
$reader->next();
return $ast;
}
function read_hash_map($reader) {
$lst = read_list($reader, '_list', '{', '}');
return call_user_func_array('_hash_map', $lst->getArrayCopy());
}
function read_form($reader) {
$token = $reader->peek();
switch ($token) {
case '\'': $reader->next();
return _list(_symbol('quote'),
read_form($reader));
case '`': $reader->next();
return _list(_symbol('quasiquote'),
read_form($reader));
case '~': $reader->next();
return _list(_symbol('unquote'),
read_form($reader));
case '~@': $reader->next();
return _list(_symbol('splice-unquote'),
read_form($reader));
case '^': $reader->next();
$meta = read_form($reader);
return _list(_symbol('with-meta'),
read_form($reader),
$meta);
case '@': $reader->next();
return _list(_symbol('deref'),
read_form($reader));
case ')': throw new Exception("unexpected ')'");
case '(': return read_list($reader);
case ']': throw new Exception("unexpected ']'");
case '[': return read_list($reader, '_vector', '[', ']');
case '}': throw new Exception("unexpected '}'");
case '{': return read_hash_map($reader);
default: return read_atom($reader);
}
}
function read_str($str) {
$tokens = tokenize($str);
if (count($tokens) === 0) { throw new BlankException(); }
return read_form(new Reader($tokens));
}
?>