Evaluator::doSpecialForm()   A
last analyzed

Complexity

Conditions 4
Paths 6

Size

Total Lines 18
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 14
cts 14
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 14
nc 6
nop 2
crap 4
1
<?php
2
namespace Desmond;
3
use Desmond\functions\EnvLoader;
4
use Desmond\data_types\ListType;
5
use Desmond\data_types\VectorType;
6
use Desmond\data_types\HashType;
7
use Desmond\data_types\SymbolType;
8
use Desmond\data_types\LambdaType;
9
use Desmond\DesmondNamespace as NS;
10
use Exception;
11
12
class Evaluator
13
{
14
    private $coreEnv;
15
    public $currentEnv;
16
17 333
    public function __construct()
18
    {
19 333
        $this->coreEnv = new Environment();
20 333
        NS::setRoot($this->coreEnv);
21 333
        $this->currentEnv = $this->coreEnv;
22 333
        EnvLoader::loadInto($this->coreEnv, 'core');
23 333
    }
24
25 331
    public function getReturn($ast)
26
    {
27 331
        if ($ast instanceof ListType) {
28 327
            return $this->evalForm($ast);
29 331
        } else if ($ast instanceof VectorType || $ast instanceof HashType) {
30 56
            return $this->evalCollection($ast);
31
        } else {
32 331
            return $this->evalAtom($ast);
33
        }
34
    }
35
36 327
    private function evalForm($form)
37
    {
38 327
        if ($form->get(0) instanceof ListType || $this->getReturn($form->get(0)) instanceof LambdaType) {
39 7
            $function = $this->getReturn($form->get(0));
40
        } else {
41 327
            $function = $form->getFunction()->value();
42
        }
43 327
        return $this->doSpecialForm($function, $form->getArgs());
44
    }
45
46 56
    public function evalCollection($ast)
47
    {
48 56
        $collection = $ast;
49 56
        foreach ($collection->value() as $key => $value) {
50 38
            $collection->set($this->getReturn($value), $key);
51
        }
52 56
        return $collection;
53
    }
54
55 327
    private function doSpecialForm($function, $args)
56
    {
57 327
        $possibilities = $this->specialFunctionList($function);
58 327
        $functionName = null;
59 327
        foreach ($possibilities as $possibility) {
60 327
            if ($possibility[0]) {
61 98
                $functionName = $possibility[1];
62 98
                break;
63
            }
64
        }
65 327
        $functionName = $functionName ? $functionName : 'EnvironmentFunction';
66 327
        $class = "Desmond\\functions\\special\\{$functionName}";
67 327
        $object = new $class();
68 327
        $object->function = $function;
69 327
        $object->currentEnv = &$this->currentEnv;
70 327
        $object->eval = $this;
71 327
        return $object->run($args);
72
    }
73
74 327
    private function specialFunctionList($function)
75
    {
76
        return [
77 327
            [$function == 'quote', 'Quote'],
78 327
            [$function == 'quasiquote', 'Quasiquote'],
79 327
            [$function == 'namespace', 'NamespaceBlock'],
80 327
            [$function == 'define', 'DefineSymbol'],
81 327
            [$function == 'let', 'Let'],
82 327
            [$function == 'do', 'DoBlock'],
83 327
            [$function == 'if', 'Conditional'],
84 327
            [$function == 'lambda', 'CreateLambda'],
85 327
            [($function instanceof LambdaType), 'RunLambda'],
86 327
            [$function == 'load-file', 'LoadFile'],
87 327
            [$function == 'eval', 'EvalBlock'],
88 327
            [$function == 'try', 'TryCatch']
89
        ];
90
    }
91
92 331
    private function evalAtom($atom)
93
    {
94 331
        if (!($atom instanceof SymbolType)) {
95 211
            return $atom;
96
        }
97
        try {
98 328
            if (false !== strpos($atom->value(), '/')) {
99 11
                $value = $this->getNamespaceValue($atom);
100
            } else {
101 323
                $value = $this->currentEnv->get($atom->value());
102
            }
103 292
            return $value;
104 128
        } catch (Exception $exeption) {
105 128
            return $atom;
106
        }
107
    }
108
109 11
    private function getNamespaceValue($atom)
110
    {
111 11
        $fullName = preg_replace('/^\//', '', $atom->value());
112 11
        $pieces = explode('/', $fullName);
113 11
        $symbol = end($pieces);
114 11
        array_pop($pieces);
115 11
        $namespace = implode('/', $pieces);
116 11
        Autoload::run($atom);
117 11
        if (NS::exists($namespace)) {
118 3
            $env = NS::get($namespace);
119 3
            $value = $env->get($symbol);
120
        } else {
121 8
            $value = $atom;
122
        }
123 11
        return $value;
124
    }
125
}
126