Lexer::readHash()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 11
cts 11
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 12
nc 4
nop 2
crap 4
1
<?php
2
namespace Desmond;
3
use Exception;
4
use Desmond\Reader;
5
use Desmond\Tokenizer;
6
use Desmond\data_types\ListType;
7
use Desmond\data_types\VectorType;
8
use Desmond\data_types\HashType;
9
use Desmond\data_types\SymbolType;
10
use Desmond\data_types\NumberType;
11
use Desmond\data_types\NilType;
12
use Desmond\data_types\TrueType;
13
use Desmond\data_types\FalseType;
14
use Desmond\data_types\StringType;
15
16
class Lexer
17
{
18
    private static $CONDITON = 0;
19
    private static $VALUE = 1;
20
21 354
    public function readString($string)
22
    {
23 354
        $tokens = Tokenizer::tokenize($string);
24 354
        if (empty($tokens)) {
25 1
            return null;
26
        }
27 353
        return $this->readForm(new Reader($tokens));
28
    }
29
30 353
    private function readForm(Reader $reader)
31
    {
32 353
        switch ($reader->peek()) {
33 353
            case '(':
34 332
                $reader->next();
35 332
                $collection = new ListType();
36 332
                return $this->readCollection($reader, $collection, ')');
37 353
            case ')':
38 1
                throw new Exception('Unexpected )');
39 352
            case '[':
40 57
                $reader->next();
41 57
                $collection = new VectorType();
42 57
                return $this->readCollection($reader, $collection, ']');
43 352
            case ']':
44 1
                throw new Exception('Unexpected ]');
45 351
            case '{':
46 33
                $reader->next();
47 33
                $hash = new HashType();
48 33
                return $this->readHash($reader, $hash);
49 351
            case '}':
50 2
                throw new Exception('Unexpected }');
51
            default:
52 350
                $form = $this->readAtom($reader->peek());
53 350
                $reader->next();
54 350
                return $form;
55
        }
56
    }
57
58 335
    private function readCollection(Reader $reader, $collection, $end)
59
    {
60 335
        while (($token = $reader->peek()) !== $end) {
61 335
            if ($token === null) {
62 2
                throw new Exception('Expected "' . $end . '", found EOF.');
63
            }
64 335
            $collection->set($this->readForm($reader));
65
        }
66 333
        $reader->next();
67 333
        return $collection;
68
    }
69
70 33
    private function readHash(Reader $reader, $hash)
71
    {
72 33
        while (($token = $reader->peek()) !== '}') {
73 26
            if ($token === null) {
74 1
                throw new Exception('Expected "}", found EOF.');
75
            }
76 26
            $key = $this->readForm($reader);
77
            try {
78 26
                $value = $this->readForm($reader);
79 1
            } catch (Exception $exeption) {
80 1
                throw new Exception('Unexpected end of hash. Every key must have a value.');
81
            }
82 26
            $hash->set($value, $key->value());
83
        }
84 31
        $reader->next();
85 31
        return $hash;
86
    }
87
88 350
    private function readAtom($token)
89
    {
90 350
        $tokenLiteral = null;
91 350
        foreach ($this->tokenTestList($token) as $test) {
92 350
            if ($test[self::$CONDITON]) {
93 350
                $tokenLiteral = $test[self::$VALUE];
94 350
                break;
95
            }
96
        }
97 350
        return $tokenLiteral;
98
    }
99
100 350
    private function tokenTestList($token)
101
    {
102
        return [
103 350
            [preg_match('/^-?(\.?[0-9]+)|([0-9]+.[0-9]+)$/', $token), new NumberType($token)],
104 350
            [preg_match('/".*"/s', $token), new StringType($token)],
105 350
            [$token === 'nil', new NilType()],
106 350
            [$token === 'true', new TrueType()],
107 350
            [$token === 'false', new FalseType()],
108 350
            [true, new SymbolType($token)] // Basically, if it's nothing else, it's a symbol.
109
        ];
110
    }
111
}
112