|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace PeacefulBit\Slate\Core; |
|
4
|
|
|
|
|
5
|
|
|
use function Nerd\Common\Arrays\toString; |
|
6
|
|
|
use function Nerd\Common\Functional\tail; |
|
7
|
|
|
|
|
8
|
|
|
use PeacefulBit\Slate\Exceptions\EvaluatorException; |
|
9
|
|
|
|
|
10
|
|
|
class Frame |
|
11
|
|
|
{ |
|
12
|
|
|
/** |
|
13
|
|
|
* @var Evaluator |
|
14
|
|
|
*/ |
|
15
|
|
|
private $evaluator; |
|
16
|
|
|
|
|
17
|
|
|
/** |
|
18
|
|
|
* @var self |
|
19
|
|
|
*/ |
|
20
|
|
|
private $parent = null; |
|
21
|
|
|
|
|
22
|
|
|
/** |
|
23
|
|
|
* @var array |
|
24
|
|
|
*/ |
|
25
|
|
|
private $table = []; |
|
26
|
|
|
|
|
27
|
|
|
/** |
|
28
|
|
|
* @var array |
|
29
|
|
|
*/ |
|
30
|
|
|
private $modules = []; |
|
31
|
|
|
|
|
32
|
|
|
/** |
|
33
|
|
|
* @param Evaluator $evaluator |
|
34
|
|
|
* @param array $table |
|
35
|
|
|
* @param Frame|null $parent |
|
36
|
|
|
*/ |
|
37
|
|
|
public function __construct(Evaluator $evaluator, array $table = [], Frame $parent = null) |
|
38
|
|
|
{ |
|
39
|
|
|
$this->evaluator = $evaluator; |
|
40
|
|
|
$this->table = $table; |
|
41
|
|
|
$this->parent = $parent; |
|
42
|
|
|
} |
|
43
|
|
|
|
|
44
|
|
|
/** |
|
45
|
|
|
* @param mixed $node |
|
46
|
|
|
* @return mixed |
|
47
|
|
|
*/ |
|
48
|
|
|
public function evaluate($node) |
|
49
|
|
|
{ |
|
50
|
|
|
// if (!$node instanceof Nodes\NodeInterface) { |
|
|
|
|
|
|
51
|
|
|
// return $node; |
|
52
|
|
|
// } |
|
53
|
|
|
|
|
54
|
|
|
// $iter = tail(function ($node) use (&$iter) { |
|
|
|
|
|
|
55
|
|
|
// if ($node instanceof Nodes\CallExpression) { |
|
56
|
|
|
// return $iter($node->evaluate($this)); |
|
57
|
|
|
// } |
|
58
|
|
|
// return $node; |
|
59
|
|
|
// }); |
|
60
|
|
|
// |
|
61
|
|
|
// return $iter($node->evaluate($this)); |
|
62
|
|
|
return $node->evaluate($this); |
|
63
|
|
|
} |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* @param mixed $node |
|
67
|
|
|
* @return mixed |
|
68
|
|
|
*/ |
|
69
|
|
|
public function valueOf($node) |
|
70
|
|
|
{ |
|
71
|
|
|
// $iter = tail(function ($node) use (&$iter) { |
|
|
|
|
|
|
72
|
|
|
// if ($node instanceof Nodes\Node) { |
|
73
|
|
|
// return $iter($this->evaluate($node)); |
|
74
|
|
|
// } |
|
75
|
|
|
// return $node; |
|
76
|
|
|
// }); |
|
77
|
|
|
// |
|
78
|
|
|
// return $iter($node); |
|
79
|
|
|
return $this->evaluate($node); |
|
80
|
|
|
} |
|
81
|
|
|
|
|
82
|
|
|
/** |
|
83
|
|
|
* @param mixed $node |
|
84
|
|
|
* @return mixed |
|
85
|
|
|
*/ |
|
86
|
|
|
public function __invoke($node) |
|
87
|
|
|
{ |
|
88
|
|
|
return $this->evaluate($node); |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
/** |
|
92
|
|
|
* @return bool |
|
93
|
|
|
*/ |
|
94
|
|
|
public function isRoot(): bool |
|
95
|
|
|
{ |
|
96
|
|
|
return is_null($this->parent); |
|
97
|
|
|
} |
|
98
|
|
|
|
|
99
|
|
|
/** |
|
100
|
|
|
* @param string $key |
|
101
|
|
|
* @return bool |
|
102
|
|
|
*/ |
|
103
|
|
|
public function has(string $key): bool |
|
104
|
|
|
{ |
|
105
|
|
|
$iter = tail(function ($key, Frame $frame) use (&$iter) { |
|
106
|
|
|
if (array_key_exists($key, $frame->table)) { |
|
107
|
|
|
return true; |
|
108
|
|
|
} |
|
109
|
|
|
if ($frame->isRoot()) { |
|
110
|
|
|
return false; |
|
111
|
|
|
} |
|
112
|
|
|
return $iter($key, $frame->parent); |
|
113
|
|
|
}); |
|
114
|
|
|
|
|
115
|
|
|
return $iter($key, $this); |
|
116
|
|
|
} |
|
117
|
|
|
|
|
118
|
|
|
/** |
|
119
|
|
|
* @param string $key |
|
120
|
|
|
* @return mixed|null |
|
121
|
|
|
* @throws EvaluatorException |
|
122
|
|
|
*/ |
|
123
|
|
|
public function get(string $key) |
|
124
|
|
|
{ |
|
125
|
|
|
$iter = tail(function ($key, Frame $frame) use (&$iter) { |
|
126
|
|
|
if (array_key_exists($key, $frame->table)) { |
|
127
|
|
|
return $frame->table[$key]; |
|
128
|
|
|
} |
|
129
|
|
|
if ($frame->isRoot()) { |
|
130
|
|
|
throw new EvaluatorException("Symbol \"$key\" not defined"); |
|
131
|
|
|
} |
|
132
|
|
|
return $iter($key, $frame->parent); |
|
133
|
|
|
}); |
|
134
|
|
|
|
|
135
|
|
|
return $iter($key, $this); |
|
136
|
|
|
} |
|
137
|
|
|
|
|
138
|
|
|
/** |
|
139
|
|
|
* @param string $key |
|
140
|
|
|
* @param mixed $value |
|
141
|
|
|
* @return void |
|
142
|
|
|
*/ |
|
143
|
|
|
public function set(string $key, $value) |
|
144
|
|
|
{ |
|
145
|
|
|
$this->table[$key] = $value; |
|
146
|
|
|
} |
|
147
|
|
|
|
|
148
|
|
|
/** |
|
149
|
|
|
* @param array $path |
|
150
|
|
|
* @return mixed |
|
151
|
|
|
* @throws EvaluatorException |
|
152
|
|
|
*/ |
|
153
|
|
|
public function getFromModule(array $path) |
|
154
|
|
|
{ |
|
155
|
|
|
$node = array_reduce($path, function ($acc, $item) { |
|
156
|
|
|
return (is_array($acc) && array_key_exists($item, $acc)) ? $acc[$item] : null; |
|
157
|
|
|
}, $this->modules); |
|
158
|
|
|
|
|
159
|
|
|
if (is_null($node)) { |
|
160
|
|
|
throw new EvaluatorException("Module not imported"); |
|
161
|
|
|
} |
|
162
|
|
|
|
|
163
|
|
|
return $node; |
|
164
|
|
|
} |
|
165
|
|
|
|
|
166
|
|
|
/** |
|
167
|
|
|
* @param array $table |
|
168
|
|
|
* @return Frame |
|
169
|
|
|
*/ |
|
170
|
|
|
public function extend(array $table = []): Frame |
|
171
|
|
|
{ |
|
172
|
|
|
return new self($this->evaluator, $table, $this); |
|
173
|
|
|
} |
|
174
|
|
|
|
|
175
|
|
|
/** |
|
176
|
|
|
* @param array $table |
|
177
|
|
|
* @return Frame |
|
178
|
|
|
*/ |
|
179
|
|
|
public function replace(array $table = []): Frame |
|
180
|
|
|
{ |
|
181
|
|
|
$this->table = array_merge($this->table, $table); |
|
182
|
|
|
return $this; |
|
183
|
|
|
} |
|
184
|
|
|
|
|
185
|
|
|
/** |
|
186
|
|
|
* @return string |
|
187
|
|
|
*/ |
|
188
|
|
|
public function __toString() |
|
189
|
|
|
{ |
|
190
|
|
|
return toString(array_keys($this->table)); |
|
191
|
|
|
} |
|
192
|
|
|
|
|
193
|
|
|
/** |
|
194
|
|
|
* @return Evaluator |
|
195
|
|
|
*/ |
|
196
|
|
|
public function getEvaluator(): Evaluator |
|
197
|
|
|
{ |
|
198
|
|
|
return $this->evaluator; |
|
199
|
|
|
} |
|
200
|
|
|
|
|
201
|
|
|
/** |
|
202
|
|
|
* @param string $name Module name to use |
|
203
|
|
|
* @param null|string $alias Module alias name |
|
204
|
|
|
*/ |
|
205
|
|
|
public function useModule(string $name, string $alias = null) |
|
206
|
|
|
{ |
|
207
|
|
|
$module = $this->getEvaluator()->getModule($name); |
|
208
|
|
|
$this->modules[$alias ?? $name] = $module; |
|
209
|
|
|
} |
|
210
|
|
|
|
|
211
|
|
|
/** |
|
212
|
|
|
* @param string $name Module name to import |
|
213
|
|
|
* @param array|null $functions List of functions to import |
|
214
|
|
|
*/ |
|
215
|
|
|
public function importModule(string $name, array $functions = null) |
|
216
|
|
|
{ |
|
217
|
|
|
$module = $this->getEvaluator()->getModule($name); |
|
218
|
|
|
|
|
219
|
|
|
$importFunctions = $functions ?? array_keys($module); |
|
220
|
|
|
|
|
221
|
|
|
array_walk($importFunctions, function ($fn) use (&$module) { |
|
222
|
|
|
$this->set($fn, $module[$fn]); |
|
223
|
|
|
}); |
|
224
|
|
|
} |
|
225
|
|
|
} |
|
226
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.