Compiler   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 117
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 2
dl 0
loc 117
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A compile() 0 15 4
B compileTree() 0 52 9
A compileSimple() 0 20 4
A buildTree() 0 19 4
A getLineLevel() 0 4 1
1
<?php
2
declare(strict_types=1);
3
namespace Thunder\Logeek;
4
5
final class Compiler
6
{
7
    public function compile(Board $board, $code): array
8
    {
9
        $lines = explode("\n", trim($code));
10
        $tree = $this->buildTree($lines);
11
        $functions = [];
12
        foreach($tree as $fn) {
13
            $tokens = explode(' ', $fn['line']);
14
            if(!$tokens || !$fn['line']) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $tokens of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
15
                continue;
16
            }
17
            $functions[$tokens[1]] = $this->compileTree($board, $fn['sub']);
18
        }
19
20
        return $functions;
21
    }
22
23
    private function compileTree(Board $board, array $tree): array
24
    {
25
        $code = [];
26
        while(true) {
27
            if(!$tree) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $tree of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
28
                break;
29
            }
30
            $line = array_shift($tree);
31
            $tokens = explode(' ', $line['line']);
32
            if(!$tokens || !$line['line']) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $tokens of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
33
                continue;
34
            }
35
            switch($tokens[0]) {
36
                case 'for': {
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
37
                    $code[] = [
38
                        'action' => 'for',
39
                        'iterations' => $tokens[1],
40
                        'program' => $this->compileTree($board, $line['sub']),
41
                    ];
42
                    break;
43
                }
44
                case 'none': { break; }
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
45
                case 'if': {
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
46
                    $else = array_shift($tree);
47
                    $code[] = [
48
                        'action' => 'if',
49
                        'left' => $tokens[1],
50
                        'operator' => $tokens[2],
51
                        'right' => $tokens[3],
52
                        'true' => $this->compileTree($board, $line['sub']),
53
                        'false' => $this->compileTree($board, $else['sub']),
54
                    ];
55
                    break;
56
                }
57
                case 'while': {
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
58
                    $code[] = [
59
                        'action' => 'while',
60
                        'left' => $tokens[1],
61
                        'operator' => $tokens[2],
62
                        'right' => $tokens[3],
63
                        'program' => $this->compileTree($board, $line['sub']),
64
                    ];
65
                    break;
66
                }
67
                default: {
0 ignored issues
show
Coding Style introduced by
DEFAULT statements must be defined using a colon

As per the PSR-2 coding standard, default statements should not be wrapped in curly braces.

switch ($expr) {
    default: { //wrong
        doSomething();
        break;
    }
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
68
                    $code = array_merge($code, $this->compileSimple($board, $tokens));
69
                }
70
            }
71
        }
72
73
        return $code;
74
    }
75
76
    private function compileSimple(Board $board, array $tokens): array
77
    {
78
        $action = array_shift($tokens);
79
        $number = 1;
80
        if(is_numeric($action)) {
81
            $number = $action;
82
            $action = array_shift($tokens);
83
        }
84
85
        $code = [];
86
        for($i = 0; $i < $number; $i++) {
87
            $args = $board->getAction($action)->getArguments();
88
            if(\count($args) !== \count($tokens)) {
89
                throw new \RuntimeException(sprintf('Action args %s does not match tokens %s!', json_encode($args), json_encode($tokens)));
90
            }
91
            $code[] = array_merge(['action' => $action], array_combine($args, $tokens));
92
        }
93
94
        return $code;
95
    }
96
97
    private function buildTree(array $lines): array
98
    {
99
        $tree = [];
100
        while($lines) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $lines of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
101
            $line = array_shift($lines);
102
            $level = $this->getLineLevel($line);
103
            $subLines = [];
104
            while($lines && $this->getLineLevel($lines[0]) > $level) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $lines of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
105
                $subLines[] = array_shift($lines);
106
            }
107
            $tree[] = [
108
                'line' => trim($line),
109
                'level' => $level,
110
                'sub' => $this->buildTree($subLines),
111
            ];
112
        }
113
114
        return $tree;
115
    }
116
117
    private function getLineLevel($line): int
118
    {
119
        return (int)((\strlen($line) - \strlen(ltrim($line))) / 2);
120
    }
121
}
122