Completed
Branch master (5720e6)
by Scott
05:47 queued 02:57
created

Lexer::readForm()   C

Complexity

Conditions 7
Paths 7

Size

Total Lines 27
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 23
cts 23
cp 1
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 24
nc 7
nop 1
crap 7
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 175
    public function readString($string)
22
    {
23 175
        $tokens = Tokenizer::tokenize($string);
24 175
        if (empty($tokens)) {
25 1
            return null;
26
        }
27 174
        return $this->readForm(new Reader($tokens));
28
    }
29
30 174
    private function readForm(Reader $reader)
31
    {
32 174
        switch ($reader->peek()) {
33 174
            case '(':
34 154
                $reader->next();
35 154
                $collection = new ListType();
36 154
                return $this->readCollection($reader, $collection, ')');
37 174
            case ')':
38 1
                throw new Exception('Unexpected )');
39 173
            case '[':
40 27
                $reader->next();
41 27
                $collection = new VectorType();
42 27
                return $this->readCollection($reader, $collection, ']');
43 173
            case ']':
44 1
                throw new Exception('Unexpected ]');
45 172
            case '{':
46 22
                $reader->next();
47 22
                $hash = new HashType();
48 22
                return $this->readHash($reader, $hash);
49 172
            case '}':
50 2
                throw new Exception('Unexpected }');
51
            default:
52 171
                $form = $this->readAtom($reader->peek());
53 171
                $reader->next();
54 171
                return $form;
55
        }
56
    }
57
58 157
    private function readCollection(Reader $reader, $collection, $end)
59
    {
60 157
        while (($token = $reader->peek()) !== $end) {
61 157
            if ($token === null) {
62 2
                throw new Exception('Expected "' . $end . '", found EOF.');
63
            }
64 157
            $collection->set($this->readForm($reader));
65
        }
66 155
        $reader->next();
67 155
        return $collection;
68
    }
69
70 22
    private function readHash(Reader $reader, $hash)
71
    {
72 22
        while (($token = $reader->peek()) !== '}') {
73 22
            if ($token === null) {
74 1
                throw new Exception('Expected "}", found EOF.');
75
            }
76 22
            $key = $this->readForm($reader);
77
            try {
78 22
                $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 22
            $hash->set($value, $key->value());
83
        }
84 20
        $reader->next();
85 20
        return $hash;
86
    }
87
88 171
    private function readAtom($token)
89
    {
90 171
        $tokenLiteral = null;
91 171
        foreach ($this->tokenTestList($token) as $test) {
92 171
            if ($test[self::$CONDITON]) {
93 171
                $tokenLiteral = $test[self::$VALUE];
94 171
                break;
95
            }
96
        }
97 171
        return $tokenLiteral;
98
    }
99
100 171
    private function tokenTestList($token)
101
    {
102
        return [
103 171
            [preg_match('/^-?(\.?[0-9]+)|([0-9]+.[0-9]+)$/', $token), new NumberType($token)],
104 171
            [preg_match('/".*"/s', $token), new StringType($token)],
105 171
            [$token === 'nil', new NilType()],
106 171
            [$token === 'true', new TrueType()],
107 171
            [$token === 'false', new FalseType()],
108 171
            [true, new SymbolType($token)] // Basically, if it's nothing else, it's a symbol.
109
        ];
110
    }
111
}
112