Completed
Push — master ( 0ada29...ecc437 )
by Richard
03:53 queued 11s
created

Tokenizer::doBrackets()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 16
rs 9.4285
cc 3
eloc 11
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 = 'LITERAL';
11
	const STRING = 'STRING';
12
	const OPEN_BRACKET = 'OPEN_BRACKET';
13
	const CLOSE_BRACKET = 'CLOSE_BRACKET';
14
	const OPEN_SQUARE_BRACKET = 'SQUARE_BRACKET';
15
	const CLOSE_SQUARE_BRACKET = 'CLOSE_SQUARE_BRACKET';
16
	const CONCAT = 'CONCAT';
17
	const ARG = 'ARG';
18
	const WHITESPACE = 'WHITESPACE';
19
	const DOT = 'DOT';
20
	const NUMERIC = 'NUMERIC';
21
	const EQUALS = 'EQUALS';
22
	const NOT = 'NOT';
23
	const OPEN_BRACE = 'OPEN_BRACE';
24
	const CLOSE_BRACE = 'CLOSE_BRACE';
25
	const BOOL = 'BOOL';
26
	const COLON = 'COLON';
27
	const SEMI_COLON = 'SEMI_COLON';
28
	const NUM_SIGN = 'NUM_SIGN';
29
	const GREATER_THAN = 'GREATER_THAN';
30
	const AT_SIGN = 'AT_SIGN';
31
32
	private $chars = [
33
		'"' => self::STRING,
34
		'\'' => self::STRING,
35
		'(' => self::OPEN_BRACKET,
36
		')' => self::CLOSE_BRACKET,
37
		'[' => self::OPEN_SQUARE_BRACKET,
38
		']' => self::CLOSE_SQUARE_BRACKET,
39
		'+' => self::CONCAT,
40
		',' => self::ARG,
41
		'.' => self::DOT,
42
		'!' => self::NOT,
43
		'=' => self::EQUALS,
44
		'{' => self::OPEN_BRACE,
45
		'}' => self::CLOSE_BRACE,
46
		':' => self::COLON,
47
		';' => self::SEMI_COLON,
48
		'#' => self::NUM_SIGN,
49
		'>' => self::GREATER_THAN,
50
		'@' => self::AT_SIGN,
51
		' ' => self::WHITESPACE,
52
		"\n" => self::WHITESPACE,
53
		"\r" => self::WHITESPACE,
54
		"\t" => self::WHITESPACE
55
	];
56
57
	public function __construct($str) {
58
		$this->str = $str;
59
	}
60
61
	public function getTokens($returnObj = true) {
62
		$tokens = [];
63
64
		for ($i = 0; $i < strlen($this->str); $i++) {
65
			$char = $this->identifyChar($this->str[$i]);
66
67
			$this->doSimpleTokens($tokens, $char);
68
			$this->doLiterals($tokens, $char, $i);
69
			$i += $this->doStrings($tokens, $char, $i);
70
			$i += $this->doBrackets($tokens, $char, $i);
71
		}
72
		if ($returnObj) return new Tokens($tokens);
73
		else return $tokens;
74
	}
75
76
	private function doSimpleTokens(&$tokens, $char) {
77
		if (in_array($char, [Tokenizer::ARG, Tokenizer::CONCAT, Tokenizer::DOT, Tokenizer::NOT,
78
			Tokenizer::EQUALS, Tokenizer::COLON, Tokenizer::SEMI_COLON, Tokenizer::WHITESPACE,
79
			Tokenizer::NUM_SIGN, Tokenizer::GREATER_THAN, Tokenizer::AT_SIGN])) {
80
			$tokens[] = ['type' => $char];
81
		}
82
	}
83
84
	private function doLiterals(&$tokens, $char, &$i) {
85
		if ($char === self::NAME) {
86
			$name = $this->str[$i];
87
			while (isset($this->str[$i+1]) && $this->identifyChar($this->str[$i+1]) == self::NAME) {
88
				$name .= $this->str[$i+1];
89
				$i++;
90
			}
91
			$this->processLiterals($tokens, $name);
92
		}
93
	}
94
95
	private function processLiterals(&$tokens, $name) {
96
		if (is_numeric($name)) $tokens[] = ['type' => self::NUMERIC, 'value' => $name];
97
		else if ($name == 'true') $tokens[] = ['type' => self::BOOL, 'value' => true];
98
		else if ($name == 'false') $tokens[] = ['type' => self::BOOL, 'value' => false];
99
		else $tokens[] = ['type' => self::NAME, 'value' => $name];
100
	}
101
102
	private function doBrackets(&$tokens, $char, $i) {
103
		$types = [
104
			self::OPEN_BRACKET => ['(', ')'],
105
			self::OPEN_BRACE => ['{', '}'],
106
			self::OPEN_SQUARE_BRACKET => ['[', ']']
107
		];
108
109
		foreach ($types as $type => $brackets) {
110
			if ($char === $type) {
111
				$contents = $this->extractBrackets($i, $brackets[0], $brackets[1]);
112
				$tokenizer = new Tokenizer($contents);
113
				$tokens[] = ['type' => $type, 'value' => $tokenizer->getTokens(), 'string' => $contents];
114
				return strlen($contents);
115
			}
116
		}
117
	}
118
119
	private function doStrings(&$tokens, $char, $i) {
120
		if ($char === self::STRING) {
121
			$string = $this->extractString($i);
122
			$length = strlen($string)+1;
123
			$char = $this->getChar($char);
124
			$string = str_replace('\\' . $char, $char, $string);
125
			$tokens[] = ['type' => self::STRING, 'value' => $string];
126
			return $length;
127
		}
128
	}
129
130
	private function extractString($pos) {
131
		$char = $this->str[$pos];
132
		$end = strpos($this->str, $char, $pos+1);
133
		while ($end !== false && $this->str[$end-1] == '\\') $end = strpos($this->str, $char, $end+1);
134
135
		return substr($this->str, $pos+1, $end-$pos-1);
136
	}
137
138
	private function extractBrackets($open, $startBracket = '(', $closeBracket = ')') {
139
		$close = strpos($this->str, $closeBracket, $open);
140
141
		$cPos = $open+1;
142
		while (($cPos = strpos($this->str, $startBracket, $cPos+1)) !== false && $cPos < $close) $close = strpos($this->str, $closeBracket, $close+1);
143
		return substr($this->str, $open+1, $close-$open-1);
144
	}
145
146
	private function identifyChar($chr) {
147
		if (isset($this->chars[$chr])) return $this->chars[$chr];
148
		else return self::NAME;
149
	}
150
151
	private function getChar($num) {
152
		$chars = array_reverse($this->chars);
153
		if (isset($chars[$num])) return $chars[$num];
154
		else return false;
155
	}
156
157
	public function serialize($tokens) {
158
		$str = '';
159
		$chars = array_flip($this->chars);
160
161
		foreach ($tokens as $token) {
162
			if (isset($chars[$token['type']])) {
163
				$str .= $chars[$token['type']];
164
			}
165
			$str .= $this->serializeValue($token);
166
		}
167
		return $str;
168
	}
169
170
	private function serializeValue($token) {
171
		if (isset($token['value'])) {
172
			if ($token['value'] instanceof Tokens) return $this->serialize($token['value']);
173
			else return $token['value'];	
174
		}			
175
	}
176
}
177