Completed
Branch master (3d6b53)
by Tom
02:56
created

Tokenizer::doLiterals()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 10
rs 9.2
cc 4
eloc 7
nc 3
nop 3
1
<?php
2
/* @description     Transformation Style Sheets - Revolutionising PHP templating    *
3
 * @author          Tom Butler [email protected]                                             *
4
 * @copyright       2015 Tom Butler <[email protected]> | https://r.je/                      *
5
 * @license         http://www.opensource.org/licenses/bsd-license.php  BSD License *
6
 * @version         1.0                                                             */
7
namespace Transphporm\Parser;
8
class Tokenizer {
9
	private $str;
10
	const NAME = 1;
11
	const STRING = 2;
12
	const STRING2 = 3;
13
	const OPEN_BRACKET = 4;
14
	const CLOSE_BRACKET = 5;
15
	const OPEN_SQUARE_BRACKET = 6;
16
	const CLOSE_SQUARE_BRACKET = 7;
17
	const CONCAT = 8;
18
	const ARG = 9;
19
	const WHITESPACE = 10;
20
	const DOT = 11;
21
	const NUMERIC = 12;
22
	const EQUALS = 13;
23
	const NOT = 14;
24
	const OPEN_BRACE = 15;
25
	const CLOSE_BRACE = 16;
26
	const BOOL = 17;
27
28
	public $chars = [
29
		'"' => self::STRING,
30
		'\'' => self::STRING2,
31
		'(' => self::OPEN_BRACKET,
32
		')' => self::CLOSE_BRACKET,
33
		'[' => self::OPEN_SQUARE_BRACKET,
34
		']' => self::CLOSE_SQUARE_BRACKET,
35
		'+' => self::CONCAT,
36
		',' => self::ARG,
37
		'.' => self::DOT,
38
		'!' => self::NOT,
39
		'=' => self::EQUALS,
40
		'{' => self::OPEN_BRACE,
41
		'}' => self::CLOSE_BRACE,
42
		' ' => self::WHITESPACE,
43
		"\n" => self::WHITESPACE,
44
		"\r" => self::WHITESPACE,
45
		"\t" => self::WHITESPACE
46
	];
47
48
	public function __construct($str) {
49
		$this->str = $str;
50
	}
51
52
	public function getTokens() {
53
		$tokens = [];
54
55
		for ($i = 0; $i < strlen($this->str); $i++) {
56
			$char = $this->identifyChar($this->str[$i]);
57
58
			$this->doSimpleTokens($tokens, $char);
59
			$this->doLiterals($tokens, $char, $i);
60
			$i += $this->doStrings($tokens, $char, $i);
61
			$i += $this->doBrackets($tokens, $char, $i);
62
		}
63
		return $tokens;
64
	}
65
66
	private function doSimpleTokens(&$tokens, $char) {
67
		if (in_array($char, [Tokenizer::ARG, Tokenizer::CONCAT, Tokenizer::DOT, Tokenizer::NOT, Tokenizer::EQUALS])) {
68
			$tokens[] = ['type' => $char];
69
		}
70
	}
71
72
	private function doLiterals(&$tokens, $char, &$i) {
73
		if ($char === self::NAME) {
74
			$name = $this->str[$i];
75
			while (isset($this->str[$i+1]) && $this->identifyChar($this->str[$i+1]) == self::NAME) {
76
				$name .= $this->str[$i+1];
77
				$i++;
78
			}
79
			$this->processLiterals($tokens, $name);
80
		}
81
	}
82
83
	private function processLiterals(&$tokens, $name) {
84
		if (is_numeric($name)) $tokens[] = ['type' => self::NUMERIC, 'value' => $name];
85
		else if ($name == 'true') $tokens[] = ['type' => self::BOOL, 'value' => true];
86
		else if ($name == 'false') $tokens[] = ['type' => self::BOOL, 'value' => false];
87
		else $tokens[] = ['type' => self::NAME, 'value' => $name];
88
	}
89
90
	private function doBrackets(&$tokens, $char, $i) {
91
		$types = [
92
			self::OPEN_BRACKET => ['(', ')'],
93
			self::OPEN_BRACE => ['{', '}'],
94
			self::OPEN_SQUARE_BRACKET => ['[', ']']
95
		];
96
97
		foreach ($types as $type => $brackets) {
98
			if ($char === $type) {
99
				$contents = $this->extractBrackets($i, $brackets[0], $brackets[1]);
100
				$tokenizer = new Tokenizer($contents);
101
				$tokens[] = ['type' => $type, 'value' => $tokenizer->getTokens()];
102
				return strlen($contents);
103
			}
104
		}
105
	}
106
107
	private function doStrings(&$tokens, $char, $i) {
108
		if (in_array($char, [self::STRING, self::STRING2])) {
109
			$string = $this->extractString($i);
110
			$length = strlen($string)+1;
111
			$char = $this->getChar($char);
112
			$string = str_replace('\\' . $char, $char, $string);
113
			$tokens[] = ['type' => self::STRING, 'value' => $string];
114
			return $length;
115
		}
116
	}
117
118
	private function extractString($pos) {
119
		$char = $this->str[$pos];
120
		$end = strpos($this->str, $char, $pos+1);
121
		while ($end !== false && $this->str[$end-1] == '\\') $end = strpos($this->str, $char, $end+1);
122
123
		return substr($this->str, $pos+1, $end-$pos-1);
124
	}
125
126
	private function extractBrackets($open, $startBracket = '(', $closeBracket = ')') {
127
		$close = strpos($this->str, $closeBracket, $open);
128
129
		$cPos = $open+1;
130
		while (($cPos = strpos($this->str, $startBracket, $cPos+1)) !== false && $cPos < $close) $close = strpos($this->str, $closeBracket, $close+1);
131
		return substr($this->str, $open+1, $close-$open-1);
132
	}
133
134
	private function identifyChar($chr) {
135
		if (isset($this->chars[$chr])) return $this->chars[$chr];
136
		else return self::NAME;
137
	}
138
139
	private function getChar($num) {
140
		$chars = array_reverse($this->chars);
141
		if (isset($chars[$num])) return $chars[$num];
142
		else return false;
143
	}
144
}
145