Alex Dima 8 سال پیش
والد
کامیت
f1f3088632
9فایلهای تغییر یافته به همراه2351 افزوده شده و 4 حذف شده
  1. 28 0
      .vscode/launch.json
  2. 1 0
      README.md
  3. 1 0
      gulpfile.js
  4. 7 0
      src/monaco.contribution.ts
  5. 418 0
      src/php.ts
  6. 1 0
      test/all.js
  7. 1882 0
      test/php.test.ts
  8. 11 4
      test/testRunner.ts
  9. 2 0
      tsconfig.json

+ 28 - 0
.vscode/launch.json

@@ -0,0 +1,28 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Unit Tests",
+            "type": "node",
+            "request": "launch",
+            "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
+            "stopOnEntry": false,
+            "args": [
+                "--grep",
+                "php"
+            ],
+            "cwd": "${workspaceRoot}",
+            "preLaunchTask": null,
+            "runtimeExecutable": null,
+            "runtimeArgs": [
+                "--nolazy"
+            ],
+            "env": {
+                "NODE_ENV": "development"
+            },
+            "console": "internalConsole",
+            "sourceMaps": false,
+            "outDir": null
+        }
+    ]
+}

+ 1 - 0
README.md

@@ -16,6 +16,7 @@ Colorization and configuration supports for multiple languages for the Monaco Ed
 * lua
 * objective-c
 * postiats
+* php
 * powershell
 * python
 * r

+ 1 - 0
gulpfile.js

@@ -61,6 +61,7 @@ gulp.task('release', ['clean-release','compile'], function() {
 			bundleOne('src/lua'),
 			bundleOne('src/markdown'),
 			bundleOne('src/objective-c'),
+			bundleOne('src/php'),
 			bundleOne('src/powershell'),
 			bundleOne('src/postiats'),
 			bundleOne('src/python'),

+ 7 - 0
src/monaco.contribution.ts

@@ -143,6 +143,13 @@ registerLanguage({
 	aliases: [ 'ATS', 'ATS/Postiats' ],
 	module: './postiats'
 });
+registerLanguage({
+	id: 'php',
+	extensions: ['.php', '.php4', '.php5', '.phtml', '.ctp'],
+	aliases: ['PHP', 'php'],
+	mimetypes: ['application/x-php'],
+	module: './php'
+});
 registerLanguage({
 	id: 'powershell',
 	extensions: [ '.ps1', '.psm1', '.psd1' ],

+ 418 - 0
src/php.ts

@@ -0,0 +1,418 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+'use strict';
+
+import IRichLanguageConfiguration = monaco.languages.LanguageConfiguration;
+import ILanguage = monaco.languages.IMonarchLanguage;
+
+export var conf:IRichLanguageConfiguration = {
+	wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
+
+	comments: {
+		lineComment: '//',
+		blockComment: ['/*', '*/']
+	},
+
+	brackets: [
+		['{', '}'],
+		['[', ']'],
+		['(', ')']
+	],
+
+	autoClosingPairs: [
+		{ open: '{', close: '}', notIn: ['string.php'] },
+		{ open: '[', close: ']', notIn: ['string.php'] },
+		{ open: '(', close: ')', notIn: ['string.php'] },
+		{ open: '"', close: '"', notIn: ['string.php'] },
+		{ open: '\'', close: '\'', notIn: ['string.php'] }
+	]
+};
+
+export const htmlTokenTypes = {
+	DELIM_START: 'start.delimiter.tag.html',
+	DELIM_END: 'end.delimiter.tag.html',
+	DELIM_COMMENT: 'comment.html',
+	getTag: (name: string) => {
+		return 'tag.html';
+	}
+};
+
+export var language = <ILanguage> {
+	defaultToken: '',
+	tokenPostfix: '',
+
+	// The main tokenizer for our languages
+	tokenizer: {
+		root: [
+			[/<\?((php)|=)?/, { token: '@rematch', switchTo: '@phpInSimpleState.root' }],
+			[/<!DOCTYPE/, 'metatag.html', '@doctype'],
+			[/<!--/, 'comment.html', '@comment'],
+			[/(<)(\w+)(\/>)/, [htmlTokenTypes.DELIM_START, 'tag.html', htmlTokenTypes.DELIM_END]],
+			[/(<)(script)/, [htmlTokenTypes.DELIM_START, { token: 'tag.html', next: '@script'} ]],
+			[/(<)(style)/, [htmlTokenTypes.DELIM_START, { token: 'tag.html', next: '@style'} ]],
+			[/(<)(\w+)/, [htmlTokenTypes.DELIM_START, { token: 'tag.html', next: '@otherTag'} ]],
+			[/(<\/)(\w+)/, [htmlTokenTypes.DELIM_START, { token: 'tag.html', next: '@otherTag' }]],
+			[/[^<]+/] // text
+		],
+
+		doctype: [
+			[/<\?((php)|=)?/, { token: '@rematch', switchTo: '@phpInSimpleState.comment' }],
+			[/[^>]+/, 'metatag.content.html' ],
+			[/>/, 'metatag.html', '@pop' ],
+		],
+
+		comment: [
+			[/<\?((php)|=)?/, { token: '@rematch', switchTo: '@phpInSimpleState.comment' }],
+			[/-->/, 'comment.html', '@pop'],
+			[/[^-]+/, 'comment.html'],
+			[/./, 'comment.html']
+		],
+
+		otherTag: [
+			[/<\?((php)|=)?/, { token: '@rematch', switchTo: '@phpInSimpleState.otherTag' }],
+			[/\/?>/, htmlTokenTypes.DELIM_END, '@pop'],
+			[/"([^"]*)"/, 'attribute.value'],
+			[/'([^']*)'/, 'attribute.value'],
+			[/[\w\-]+/, 'attribute.name'],
+			[/=/, 'delimiter'],
+			[/[ \t\r\n]+/], // whitespace
+		],
+
+		// -- BEGIN <script> tags handling
+
+		// After <script
+		script: [
+			[/<\?((php)|=)?/, { token: '@rematch', switchTo: '@phpInSimpleState.script' }],
+			[/type/, 'attribute.name', '@scriptAfterType'],
+			[/"([^"]*)"/, 'attribute.value'],
+			[/'([^']*)'/, 'attribute.value'],
+			[/[\w\-]+/, 'attribute.name'],
+			[/=/, 'delimiter'],
+			[/>/, { token: htmlTokenTypes.DELIM_END, next: '@scriptEmbedded.text/javascript', nextEmbedded: 'text/javascript'} ],
+			[/[ \t\r\n]+/], // whitespace
+			[/(<\/)(script\s*)(>)/, [ htmlTokenTypes.DELIM_START, 'tag.html', { token: htmlTokenTypes.DELIM_END, next: '@pop' } ]]
+		],
+
+		// After <script ... type
+		scriptAfterType: [
+			[/<\?((php)|=)?/, { token: '@rematch', switchTo: '@phpInSimpleState.scriptAfterType' }],
+			[/=/,'delimiter', '@scriptAfterTypeEquals'],
+			[/[ \t\r\n]+/], // whitespace
+			[/<\/script\s*>/, { token: '@rematch', next: '@pop' }]
+		],
+
+		// After <script ... type =
+		scriptAfterTypeEquals: [
+			[/<\?((php)|=)?/, { token: '@rematch', switchTo: '@phpInSimpleState.scriptAfterTypeEquals' }],
+			[/"([^"]*)"/, { token: 'attribute.value', switchTo: '@scriptWithCustomType.$1' } ],
+			[/'([^']*)'/, { token: 'attribute.value', switchTo: '@scriptWithCustomType.$1' } ],
+			[/[ \t\r\n]+/], // whitespace
+			[/<\/script\s*>/, { token: '@rematch', next: '@pop' }]
+		],
+
+		// After <script ... type = $S2
+		scriptWithCustomType: [
+			[/<\?((php)|=)?/, { token: '@rematch', switchTo: '@phpInSimpleState.scriptWithCustomType.$S2' }],
+			[/>/, { token: htmlTokenTypes.DELIM_END, next: '@scriptEmbedded.$S2', nextEmbedded: '$S2'}],
+			[/[ \t\r\n]+/], // whitespace
+			[/<\/script\s*>/, { token: '@rematch', next: '@pop' }]
+		],
+
+		scriptEmbedded: [
+			[/<\?((php)|=)?/, { token: '@rematch', switchTo: '@phpInEmbeddedState.scriptEmbedded.$S2', nextEmbedded: '@pop' }],
+			[/<\/script/, { token: '@rematch', next: '@pop', nextEmbedded: '@pop' }]
+		],
+
+		// -- END <script> tags handling
+
+
+		// -- BEGIN <style> tags handling
+
+		// After <style
+		style: [
+			[/<\?((php)|=)?/, { token: '@rematch', switchTo: '@phpInSimpleState.style' }],
+			[/type/, 'attribute.name', '@styleAfterType'],
+			[/"([^"]*)"/, 'attribute.value'],
+			[/'([^']*)'/, 'attribute.value'],
+			[/[\w\-]+/, 'attribute.name'],
+			[/=/, 'delimiter'],
+			[/>/, { token: htmlTokenTypes.DELIM_END, next: '@styleEmbedded.text/css', nextEmbedded: 'text/css'} ],
+			[/[ \t\r\n]+/], // whitespace
+			[/(<\/)(style\s*)(>)/, [htmlTokenTypes.DELIM_START, 'tag.html', { token: htmlTokenTypes.DELIM_END, next: '@pop' } ]]
+		],
+
+		// After <style ... type
+		styleAfterType: [
+			[/<\?((php)|=)?/, { token: '@rematch', switchTo: '@phpInSimpleState.styleAfterType' }],
+			[/=/,'delimiter', '@styleAfterTypeEquals'],
+			[/[ \t\r\n]+/], // whitespace
+			[/<\/style\s*>/, { token: '@rematch', next: '@pop' }]
+		],
+
+		// After <style ... type =
+		styleAfterTypeEquals: [
+			[/<\?((php)|=)?/, { token: '@rematch', switchTo: '@phpInSimpleState.styleAfterTypeEquals' }],
+			[/"([^"]*)"/, { token: 'attribute.value', switchTo: '@styleWithCustomType.$1' } ],
+			[/'([^']*)'/, { token: 'attribute.value', switchTo: '@styleWithCustomType.$1' } ],
+			[/[ \t\r\n]+/], // whitespace
+			[/<\/style\s*>/, { token: '@rematch', next: '@pop' }]
+		],
+
+		// After <style ... type = $S2
+		styleWithCustomType: [
+			[/<\?((php)|=)?/, { token: '@rematch', switchTo: '@phpInSimpleState.styleWithCustomType.$S2' }],
+			[/>/, { token: htmlTokenTypes.DELIM_END, next: '@styleEmbedded.$S2', nextEmbedded: '$S2'}],
+			[/[ \t\r\n]+/], // whitespace
+			[/<\/style\s*>/, { token: '@rematch', next: '@pop' }]
+		],
+
+		styleEmbedded: [
+			[/<\?((php)|=)?/, { token: '@rematch', switchTo: '@phpInEmbeddedState.styleEmbedded.$S2', nextEmbedded: '@pop' }],
+			[/<\/style/, { token: '@rematch', next: '@pop', nextEmbedded: '@pop' }]
+		],
+
+		// -- END <style> tags handling
+
+
+		phpInSimpleState: [
+			[/<\?((php)|=)?/, 'metatag.php'],
+			[/\?>/, { token: 'metatag.php', switchTo: '@$S2.$S3' }],
+			{ include: 'phpRoot' }
+		],
+
+		phpInEmbeddedState: [
+			[/<\?((php)|=)?/, 'metatag.php'],
+			[/\?>/, { token: 'metatag.php', switchTo: '@$S2.$S3', nextEmbedded: '$S3' }],
+			{ include: 'phpRoot' }
+		],
+
+		phpRoot: [
+			[/[a-zA-Z_]\w*/, {
+				cases: {
+					'@phpKeywords': { token:'keyword.php' },
+					'@phpCompileTimeConstants': { token: 'constant.php'},
+					'@default': 'identifier.php'
+				}
+			}],
+			[/[$a-zA-Z_]\w*/, {
+				cases: {
+					'@phpPreDefinedVariables': { token:'variable.predefined.php' },
+					'@default': 'variable.php'
+				}
+			}],
+
+			// brackets
+			[/[{}]/, 'delimiter.bracket.php' ],
+			[/[\[\]]/, 'delimiter.array.php' ],
+			[/[()]/, 'delimiter.parenthesis.php' ],
+
+			// whitespace
+			[/[ \t\r\n]+/],
+
+			// comments
+			[/#/, 'comment.php', '@phpLineComment'],
+			[/\/\//, 'comment.php', '@phpLineComment'],
+
+			// block comments
+			[/\/\*/, 'comment.php', '@phpComment' ],
+
+			// strings
+			[/"/, 'string.php', '@phpDoubleQuoteString' ],
+			[/'/, 'string.php', '@phpSingleQuoteString' ],
+
+			// delimiters
+			[/[\+\-\*\%\&\|\^\~\!\=\<\>\/\?\;\:\.\,\@]/, 'delimiter.php' ],
+
+			// numbers
+			[/\d*\d+[eE]([\-+]?\d+)?/, 'number.float.php'],
+			[/\d*\.\d+([eE][\-+]?\d+)?/, 'number.float.php'],
+			[/0[xX][0-9a-fA-F']*[0-9a-fA-F]/, 'number.hex.php'],
+			[/0[0-7']*[0-7]/, 'number.octal.php'],
+			[/0[bB][0-1']*[0-1]/, 'number.binary.php'],
+			[/\d[\d']*/, 'number.php'],
+			[/\d/, 'number.php'],
+		],
+
+		phpComment: [
+			[/\*\//, 'comment.php', '@pop'],
+			[/[^*]+/, 'comment.php'],
+			[/./, 'comment.php']
+		],
+
+		phpLineComment: [
+			[/\?>/, { token: '@rematch', next: '@pop' }],
+			[/.$/, 'comment.php', '@pop'],
+			[/[^?]+$/, 'comment.php', '@pop'],
+			[/[^?]+/, 'comment.php'],
+			[/./, 'comment.php']
+		],
+
+		phpDoubleQuoteString: [
+			[/[^\\"]+/,  'string.php'],
+			[/@escapes/, 'string.escape.php'],
+			[/\\./,      'string.escape.invalid.php'],
+			[/"/,        'string.php', '@pop' ]
+		],
+
+		phpSingleQuoteString: [
+			[/[^\\']+/,  'string.php'],
+			[/@escapes/, 'string.escape.php'],
+			[/\\./,      'string.escape.invalid.php'],
+			[/'/,        'string.php', '@pop' ]
+		],
+	},
+
+	phpKeywords: [
+		'abstract', 'and', 'array', 'as', 'break',
+		'callable', 'case', 'catch', 'cfunction', 'class', 'clone',
+		'const', 'continue', 'declare', 'default', 'do',
+		'else', 'elseif', 'enddeclare', 'endfor', 'endforeach',
+		'endif', 'endswitch', 'endwhile', 'extends', 'false', 'final',
+		'for', 'foreach', 'function', 'global', 'goto',
+		'if', 'implements', 'interface', 'instanceof', 'insteadof',
+		'namespace', 'new', 'null', 'object', 'old_function', 'or', 'private',
+		'protected', 'public', 'resource', 'static', 'switch', 'throw', 'trait',
+		'try', 'true', 'use', 'var', 'while', 'xor',
+		'die', 'echo', 'empty', 'exit', 'eval',
+		'include', 'include_once', 'isset', 'list', 'require',
+		'require_once', 'return', 'print', 'unset', 'yield',
+		'__construct'
+	],
+
+	phpCompileTimeConstants: [
+		'__CLASS__',
+		'__DIR__',
+		'__FILE__',
+		'__LINE__',
+		'__NAMESPACE__',
+		'__METHOD__',
+		'__FUNCTION__',
+		'__TRAIT__'
+	],
+
+	phpPreDefinedVariables: [
+		'$GLOBALS',
+		'$_SERVER',
+		'$_GET',
+		'$_POST',
+		'$_FILES',
+		'$_REQUEST',
+		'$_SESSION',
+		'$_ENV',
+		'$_COOKIE',
+		'$php_errormsg',
+		'$HTTP_RAW_POST_DATA',
+		'$http_response_header',
+		'$argc',
+		'$argv'
+	],
+
+	escapes:  /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
+};
+
+// TESTED WITH
+
+// <style type="text/css" >
+//   .boo { background: blue;
+//   <?=''?>
+//   }
+//   .boo { background: blue;  <?=''?>  }
+// </style>
+// <!--
+
+// <?= '' ?>
+
+// -->
+// <?php
+// // The next line contains a syntax error:
+// __construct
+// if () {
+// 	return "The parser recovers from this type of syntax error";
+// }
+// ?>
+// <html>
+// <head>
+// 	<title <?=''?>>Example page</title>
+//   <style <?=''?>>
+//     .boo { background: blue; <?=''?> }
+//   </style>
+// </head>
+
+// <body>
+
+// <script <?=''?> type<?=''?>=<?=''?>"text/javascript"<?=''?>>
+// 	// Some PHP embedded inside JS
+// 	// Generated <?=date('l, F jS, Y')?>
+
+// 	var server_token = <?=rand(5, 10000)?>
+// 	if (typeof server_token === 'number') {
+// 		alert('token: ' + server_token);
+// 	}
+// </script>
+
+// <div>
+// Hello
+// <? if (isset($user)) { ?>
+// 	<b><?=$user?></b>
+// <? } else { ?>
+// 	<i>guest</i>
+// <? } ?>
+// !
+// </div>
+
+// <?php
+
+// 	/* Example PHP file
+// 	multiline comment
+// 	*/
+
+//  # Another single line comment
+
+// 	$cards = array("ah", "ac", "ad", "as",
+// 		"2h", "2c", "2d", "2s",
+// 		"3h", "3c", "3d", "3s",
+// 		"4h", "4c", "4d", "4s",
+// 		"5h", "5c", "5d", "5s",
+// 		"6h", "6c", "6d", "6s",
+// 		"7h", "7c", "7d", "7s",
+// 		"8h", "8c", "8d", "8s",
+// 		"9h", "9c", "9d", "9s",
+// 		"th", "tc", "td", "ts",
+// 		"jh", "jc", "jd", "js",
+// 		"qh", "qc", "qd", "qs",
+// 		"kh", "kc", "kd", "ks");
+
+// 	srand(time());
+
+// 	for($i = 0; $i < 52; $i++) {
+// 		$count = count($cards);
+// 		$random = (rand()%$count);
+
+// 		if($cards[$random] == "") {
+// 			$i--;
+// 		} else {
+// 			$deck[] = $cards[$random];
+// 			$cards[$random] = "";
+// 		}
+// 	}
+// $_GET
+// __CLASS__
+
+// 	srand(time());
+// 	$starting_point = (rand()%51);
+// 	print("Starting point for cut cards is: $starting_point<p>");
+
+// 	// display shuffled cards (EXAMPLE ONLY)
+// 	for ($index = 0; $index < 52; $index++) {
+// 		if ($starting_point == 52) { $starting_point = 0; }
+// 		print("Uncut Point: <strong>$deck[$index]</strong> ");
+// 		print("Starting Point: <strong>$deck[$starting_point]</strong><br>");
+// 		$starting_point++;
+// 	}
+// ?>
+
+// </body>
+// </html>

+ 1 - 0
test/all.js

@@ -35,6 +35,7 @@ requirejs([
 		'out/test/lua.test',
 		'out/test/markdown.test',
 		'out/test/objective-c.test',
+		'out/test/php.test',
 		'out/test/postiats.test',
 		'out/test/powershell.test',
 		'out/test/python.test',

+ 1882 - 0
test/php.test.ts

@@ -0,0 +1,1882 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+'use strict';
+
+import {testTokenization} from './testRunner';
+import {htmlTokenTypes} from '../src/php';
+
+testTokenization(['php', 'css'], [
+	// Bug 13596:[ErrorTelemetry] Stream did not advance while tokenizing. Mode id is php (stuck)
+	// We're testing the fact that tokenize does not throw
+	[
+	{ line: '<?php', tokens: [{ startIndex:0, type: "metatag.php" }]},
+	{ line: '"', tokens: [{ startIndex:0, type: "string.php"}]},
+	{ line: '\\', tokens: [{startIndex:0, type:""}]}
+	],
+
+	// Blocks
+	[{
+	line: '<?php',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?=',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php /* comment */ ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'comment.php' },
+		{ startIndex:19, type: '' },
+		{ startIndex:20, type: 'metatag.php' }
+	]}],
+
+	// Variables
+	[{
+	line: '<?php $abc = 5; ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'delimiter.php' },
+		{ startIndex:12, type: '' },
+		{ startIndex:13, type: 'number.php' },
+		{ startIndex:14, type: 'delimiter.php' },
+		{ startIndex:15, type: '' },
+		{ startIndex:16, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $a = "chris"; ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'delimiter.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'string.php' },
+		{ startIndex:18, type: 'delimiter.php' },
+		{ startIndex:19, type: '' },
+		{ startIndex:20, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $myVar = -10; ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.php' },
+		{ startIndex:12, type: '' },
+		{ startIndex:13, type: 'delimiter.php' },
+		{ startIndex:14, type: '' },
+		{ startIndex:15, type: 'delimiter.php' },
+		{ startIndex:16, type: 'number.php' },
+		{ startIndex:18, type: 'delimiter.php' },
+		{ startIndex:19, type: '' },
+		{ startIndex:20, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $myVar = 5 + (10 * 2); ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.php' },
+		{ startIndex:12, type: '' },
+		{ startIndex:13, type: 'delimiter.php' },
+		{ startIndex:14, type: '' },
+		{ startIndex:15, type: 'number.php' },
+		{ startIndex:16, type: '' },
+		{ startIndex:17, type: 'delimiter.php' },
+		{ startIndex:18, type: '' },
+		{ startIndex:19, type: 'delimiter.parenthesis.php' },
+		{ startIndex:20, type: 'number.php' },
+		{ startIndex:22, type: '' },
+		{ startIndex:23, type: 'delimiter.php' },
+		{ startIndex:24, type: '' },
+		{ startIndex:25, type: 'number.php' },
+		{ startIndex:26, type: 'delimiter.parenthesis.php' },
+		{ startIndex:27, type: 'delimiter.php' },
+		{ startIndex:28, type: '' },
+		{ startIndex:29, type: 'metatag.php' }
+	]}],
+
+	// Keywords
+	[{
+	line: '<?php function myFunc() { } ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:14, type: '' },
+		{ startIndex:15, type: 'identifier.php' },
+		{ startIndex:21, type: 'delimiter.parenthesis.php' },
+		{ startIndex:23, type: '' },
+		{ startIndex:24, type: 'delimiter.bracket.php' },
+		{ startIndex:25, type: '' },
+		{ startIndex:26, type: 'delimiter.bracket.php' },
+		{ startIndex:27, type: '' },
+		{ startIndex:28, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php function ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:14, type: '' },
+		{ startIndex:15, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php if ($start > 52) { $start = 0; } ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'delimiter.parenthesis.php' },
+		{ startIndex:10, type: 'variable.php' },
+		{ startIndex:16, type: '' },
+		{ startIndex:17, type: 'delimiter.php' },
+		{ startIndex:18, type: '' },
+		{ startIndex:19, type: 'number.php' },
+		{ startIndex:21, type: 'delimiter.parenthesis.php' },
+		{ startIndex:22, type: '' },
+		{ startIndex:23, type: 'delimiter.bracket.php' },
+		{ startIndex:24, type: '' },
+		{ startIndex:25, type: 'variable.php' },
+		{ startIndex:31, type: '' },
+		{ startIndex:32, type: 'delimiter.php' },
+		{ startIndex:33, type: '' },
+		{ startIndex:34, type: 'number.php' },
+		{ startIndex:35, type: 'delimiter.php' },
+		{ startIndex:36, type: '' },
+		{ startIndex:37, type: 'delimiter.bracket.php' },
+		{ startIndex:38, type: '' },
+		{ startIndex:39, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php if (true) { $start = 0; } ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'delimiter.parenthesis.php' },
+		{ startIndex:10, type: 'keyword.php' },
+		{ startIndex:14, type: 'delimiter.parenthesis.php' },
+		{ startIndex:15, type: '' },
+		{ startIndex:16, type: 'delimiter.bracket.php' },
+		{ startIndex:17, type: '' },
+		{ startIndex:18, type: 'variable.php' },
+		{ startIndex:24, type: '' },
+		{ startIndex:25, type: 'delimiter.php' },
+		{ startIndex:26, type: '' },
+		{ startIndex:27, type: 'number.php' },
+		{ startIndex:28, type: 'delimiter.php' },
+		{ startIndex:29, type: '' },
+		{ startIndex:30, type: 'delimiter.bracket.php' },
+		{ startIndex:31, type: '' },
+		{ startIndex:32, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php abstract ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:14, type: '' },
+		{ startIndex:15, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php and ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:9, type: '' },
+		{ startIndex:10, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php array ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php as ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php break ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php case ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php catch ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php cfunction ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:15, type: '' },
+		{ startIndex:16, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php class ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php clone ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php const ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php continue ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:14, type: '' },
+		{ startIndex:15, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php declare ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:13, type: '' },
+		{ startIndex:14, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php default ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:13, type: '' },
+		{ startIndex:14, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php do ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php else ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php elseif ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:12, type: '' },
+		{ startIndex:13, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php enddeclare ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:16, type: '' },
+		{ startIndex:17, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php endfor ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:12, type: '' },
+		{ startIndex:13, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php endforeach ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:16, type: '' },
+		{ startIndex:17, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php endif ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php endswitch ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:15, type: '' },
+		{ startIndex:16, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php endwhile ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:14, type: '' },
+		{ startIndex:15, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php extends ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:13, type: '' },
+		{ startIndex:14, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php false ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php final ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php for ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:9, type: '' },
+		{ startIndex:10, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php foreach ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:13, type: '' },
+		{ startIndex:14, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php function ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:14, type: '' },
+		{ startIndex:15, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php global ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:12, type: '' },
+		{ startIndex:13, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php goto ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php if ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php implements ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:16, type: '' },
+		{ startIndex:17, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php interface ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:15, type: '' },
+		{ startIndex:16, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php instanceof ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:16, type: '' },
+		{ startIndex:17, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php namespace ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:15, type: '' },
+		{ startIndex:16, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php new ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:9, type: '' },
+		{ startIndex:10, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php null ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php object ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:12, type: '' },
+		{ startIndex:13, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php old_function ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:18, type: '' },
+		{ startIndex:19, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php or ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php private ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:13, type: '' },
+		{ startIndex:14, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php protected ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:15, type: '' },
+		{ startIndex:16, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php public ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:12, type: '' },
+		{ startIndex:13, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php resource ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:14, type: '' },
+		{ startIndex:15, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php static ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:12, type: '' },
+		{ startIndex:13, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php switch ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:12, type: '' },
+		{ startIndex:13, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php throw ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php try ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:9, type: '' },
+		{ startIndex:10, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php true ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php use ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:9, type: '' },
+		{ startIndex:10, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php var ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:9, type: '' },
+		{ startIndex:10, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php while ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php xor ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:9, type: '' },
+		{ startIndex:10, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php die ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:9, type: '' },
+		{ startIndex:10, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php echo ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php empty ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php exit ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php eval ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php include ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:13, type: '' },
+		{ startIndex:14, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php include_once ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:18, type: '' },
+		{ startIndex:19, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php isset ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php list ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php require ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:13, type: '' },
+		{ startIndex:14, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php require_once ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:18, type: '' },
+		{ startIndex:19, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php return ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:12, type: '' },
+		{ startIndex:13, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php print ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php unset ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php __construct ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:17, type: '' },
+		{ startIndex:18, type: 'metatag.php' }
+	]}],
+
+	// Compile Time Constants
+	[{
+	line: '<?php __FILE__ ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'constant.php' },
+		{ startIndex:14, type: '' },
+		{ startIndex:15, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $myscript = __FILE__; ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.php' },
+		{ startIndex:15, type: '' },
+		{ startIndex:16, type: 'delimiter.php' },
+		{ startIndex:17, type: '' },
+		{ startIndex:18, type: 'constant.php' },
+		{ startIndex:26, type: 'delimiter.php' },
+		{ startIndex:27, type: '' },
+		{ startIndex:28, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php __CLASS__ ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'constant.php' },
+		{ startIndex:15, type: '' },
+		{ startIndex:16, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php __DIR__ ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'constant.php' },
+		{ startIndex:13, type: '' },
+		{ startIndex:14, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php __LINE__ ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'constant.php' },
+		{ startIndex:14, type: '' },
+		{ startIndex:15, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php __NAMESPACE__ ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'constant.php' },
+		{ startIndex:19, type: '' },
+		{ startIndex:20, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php __METHOD__ ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'constant.php' },
+		{ startIndex:16, type: '' },
+		{ startIndex:17, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php __FUNCTION__ ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'constant.php' },
+		{ startIndex:18, type: '' },
+		{ startIndex:19, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php __TRAIT__ ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'constant.php' },
+		{ startIndex:15, type: '' },
+		{ startIndex:16, type: 'metatag.php' }
+	]}],
+
+	// Predefined Variables
+	[{
+	line: '<?php $_ENV ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.predefined.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php echo $_ENV; ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'keyword.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'variable.predefined.php' },
+		{ startIndex:16, type: 'delimiter.php' },
+		{ startIndex:17, type: '' },
+		{ startIndex:18, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $GLOBALS ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.predefined.php' },
+		{ startIndex:14, type: '' },
+		{ startIndex:15, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $_SERVER ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.predefined.php' },
+		{ startIndex:14, type: '' },
+		{ startIndex:15, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $_GET ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.predefined.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $_POST ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.predefined.php' },
+		{ startIndex:12, type: '' },
+		{ startIndex:13, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $_FILES ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.predefined.php' },
+		{ startIndex:13, type: '' },
+		{ startIndex:14, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $_REQUEST ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.predefined.php' },
+		{ startIndex:15, type: '' },
+		{ startIndex:16, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $_SESSION ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.predefined.php' },
+		{ startIndex:15, type: '' },
+		{ startIndex:16, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $_ENV ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.predefined.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $_COOKIE ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.predefined.php' },
+		{ startIndex:14, type: '' },
+		{ startIndex:15, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $php_errormsg ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.predefined.php' },
+		{ startIndex:19, type: '' },
+		{ startIndex:20, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $HTTP_RAW_POST_DATA ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.predefined.php' },
+		{ startIndex:25, type: '' },
+		{ startIndex:26, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $http_response_header ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.predefined.php' },
+		{ startIndex:27, type: '' },
+		{ startIndex:28, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $argc ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.predefined.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $argv ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.predefined.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	// Comments - single line
+	[{
+	line: '<?php // a',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'comment.php' }
+	]}],
+
+	[{
+	line: '<?php / / / not a comment',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'delimiter.php' },
+		{ startIndex:7, type: '' },
+		{ startIndex:8, type: 'delimiter.php' },
+		{ startIndex:9, type: '' },
+		{ startIndex:10, type: 'delimiter.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'identifier.php' },
+		{ startIndex:15, type: '' },
+		{ startIndex:16, type: 'identifier.php' },
+		{ startIndex:17, type: '' },
+		{ startIndex:18, type: 'identifier.php' },
+	]}],
+
+	[{
+	line: '<?php    // a comment',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:9, type: 'comment.php' }
+	]}],
+
+	[{
+	line: '<?php // a comment',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'comment.php' }
+	]}],
+
+	[{
+	line: '<?php //sticky comment',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'comment.php' }
+	]}],
+
+	[{
+	line: '<?php /almost a comment',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'delimiter.php' },
+		{ startIndex:7, type: 'identifier.php' },
+		{ startIndex:13, type: '' },
+		{ startIndex:14, type: 'identifier.php' },
+		{ startIndex:15, type: '' },
+		{ startIndex:16, type: 'identifier.php' },
+	]}],
+
+	[{
+	line: '<?php $x = 1; // my comment // is a nice one',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'delimiter.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'number.php' },
+		{ startIndex:12, type: 'delimiter.php' },
+		{ startIndex:13, type: '' },
+		{ startIndex:14, type: 'comment.php' }
+	]}],
+
+	// Comments - range comment, single line
+	[{
+	line: '<?php /* a simple comment */ ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'comment.php' },
+		{ startIndex:28, type: '' },
+		{ startIndex:29, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $x = /* a simple comment */ 1; ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'delimiter.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'comment.php' },
+		{ startIndex:33, type: '' },
+		{ startIndex:34, type: 'number.php' },
+		{ startIndex:35, type: 'delimiter.php' },
+		{ startIndex:36, type: '' },
+		{ startIndex:37, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $x = /* comment */ 1; */ ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'delimiter.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'comment.php' },
+		{ startIndex:24, type: '' },
+		{ startIndex:25, type: 'number.php' },
+		{ startIndex:26, type: 'delimiter.php' },
+		{ startIndex:27, type: '' },
+		{ startIndex:28, type: 'delimiter.php' },
+		{ startIndex:30, type: '' },
+		{ startIndex:31, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $x = /**/; ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'delimiter.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'comment.php' },
+		{ startIndex:15, type: 'delimiter.php' },
+		{ startIndex:16, type: '' },
+		{ startIndex:17, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $x = /*/;',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'delimiter.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'comment.php' }
+	]}],
+
+	// Comments - range comment, multi lines
+	[{
+	line: '<?php /* a multiline comment',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'comment.php' }
+	]}, {
+	line: 'can actually span',
+	tokens: [
+		{ startIndex:0, type: 'comment.php' }
+	]}, {
+	line: 'multiple lines */',
+	tokens: [
+		{ startIndex:0, type: 'comment.php' }
+	]}],
+
+	[{
+	line: '<?php $x = /* start a comment',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'delimiter.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'comment.php' }
+	]}, {
+	line: ' a ',
+	tokens: [
+		{ startIndex:0, type: 'comment.php' }
+	]}, {
+	line: 'and end it */ var a = 2;',
+	tokens: [
+		{ startIndex:0, type: 'comment.php' },
+		{ startIndex:13, type: '' },
+		{ startIndex:14, type: 'keyword.php' },
+		{ startIndex:17, type: '' },
+		{ startIndex:18, type: 'identifier.php' },
+		{ startIndex:19, type: '' },
+		{ startIndex:20, type: 'delimiter.php' },
+		{ startIndex:21, type: '' },
+		{ startIndex:22, type: 'number.php' },
+		{ startIndex:23, type: 'delimiter.php' }
+	]}],
+
+	// Strings
+	[{
+	line: '<?php $a = \'a\'; ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'delimiter.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'string.php' },
+		{ startIndex:14, type: 'delimiter.php' },
+		{ startIndex:15, type: '' },
+		{ startIndex:16, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php \'use strict\'; ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'string.php' },
+		{ startIndex:18, type: 'delimiter.php' },
+		{ startIndex:19, type: '' },
+		{ startIndex:20, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php $b = $a + " \'cool\'  " ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'delimiter.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'variable.php' },
+		{ startIndex:13, type: '' },
+		{ startIndex:14, type: 'delimiter.php' },
+		{ startIndex:15, type: '' },
+		{ startIndex:16, type: 'string.php' },
+		{ startIndex:27, type: '' },
+		{ startIndex:28, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php \'\'\'',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'string.php' },
+	]}],
+
+	[{
+	line: '<?php "multiline',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'string.php' }
+	]}, {
+	line: 'strings";',
+	tokens: [
+		{ startIndex:0, type: 'string.php' },
+		{ startIndex:8, type: 'delimiter.php' }
+	]}],
+
+	// Numbers
+	[{
+	line: '<?php 0 ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'number.php' },
+		{ startIndex:7, type: '' },
+		{ startIndex:8, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php 0+0 ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'number.php' },
+		{ startIndex:7, type: 'delimiter.php' },
+		{ startIndex:8, type: 'number.php' },
+		{ startIndex:9, type: '' },
+		{ startIndex:10, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php 100+10 ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'number.php' },
+		{ startIndex:9, type: 'delimiter.php' },
+		{ startIndex:10, type: 'number.php' },
+		{ startIndex:12, type: '' },
+		{ startIndex:13, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php 0 + 0 ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'number.php' },
+		{ startIndex:7, type: '' },
+		{ startIndex:8, type: 'delimiter.php' },
+		{ startIndex:9, type: '' },
+		{ startIndex:10, type: 'number.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php 0123 ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'number.octal.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php 01239 ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'number.octal.php' },
+		{ startIndex:10, type: 'number.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php 0x ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'number.php' },
+		{ startIndex:7, type: 'identifier.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php 0x123 ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'number.hex.php' },
+		{ startIndex:11, type: '' },
+		{ startIndex:12, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php 0b1 ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'number.binary.php' },
+		{ startIndex:9, type: '' },
+		{ startIndex:10, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php { } ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'delimiter.bracket.php' },
+		{ startIndex:7, type: '' },
+		{ startIndex:8, type: 'delimiter.bracket.php' },
+		{ startIndex:9, type: '' },
+		{ startIndex:10, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php [1,2,3] ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'delimiter.array.php' },
+		{ startIndex:7, type: 'number.php' },
+		{ startIndex:8, type: 'delimiter.php' },
+		{ startIndex:9, type: 'number.php' },
+		{ startIndex:10, type: 'delimiter.php' },
+		{ startIndex:11, type: 'number.php' },
+		{ startIndex:12, type: 'delimiter.array.php' },
+		{ startIndex:13, type: '' },
+		{ startIndex:14, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php foo(123);',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'identifier.php' },
+		{ startIndex:9, type: 'delimiter.parenthesis.php' },
+		{ startIndex:10, type: 'number.php' },
+		{ startIndex:13, type: 'delimiter.parenthesis.php' },
+		{ startIndex:14, type: 'delimiter.php' }
+	]}],
+
+	[{
+	line: '<?php $x = "[{()}]" ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'delimiter.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'string.php' },
+		{ startIndex:19, type: '' },
+		{ startIndex:20, type: 'metatag.php' }
+	]}],
+
+	// Comments - comment with sharp
+	[{
+	line: '<?php # a',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'comment.php' }
+	]}],
+
+	[{
+	line: '<?php ## a',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'comment.php' }
+	]}],
+
+	[{
+	line: '<?php    # a comment',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:9, type: 'comment.php' }
+	]}],
+
+	[{
+	line: '<?php #sticky comment',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'comment.php' }
+	]}],
+
+	[{
+	line: '<?php $x = 1; # my comment // is a nice one',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'variable.php' },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'delimiter.php' },
+		{ startIndex:10, type: '' },
+		{ startIndex:11, type: 'number.php' },
+		{ startIndex:12, type: 'delimiter.php' },
+		{ startIndex:13, type: '' },
+		{ startIndex:14, type: 'comment.php' }
+	]}],
+
+	[{
+	line: '<?php # comment?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'comment.php' },
+		{ startIndex:15, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<?php # comment? ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'comment.php' },
+		{ startIndex:17, type: 'metatag.php' }
+	]}],
+
+	// 3-languages parser
+
+	// php
+	[{
+	line: '<?=\'hi\'?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:3, type: 'string.php' },
+		{ startIndex:7, type: 'metatag.php' }
+	]}],
+
+	// php/html/php
+	[{
+	line: '<?php5+3?><br/><?=1?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: 'number.php' },
+		{ startIndex:6, type: 'delimiter.php' },
+		{ startIndex:7, type: 'number.php' },
+		{ startIndex:8, type: 'metatag.php' },
+		{ startIndex:10, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:11, type: htmlTokenTypes.getTag('br') },
+		{ startIndex:13, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:15, type: 'metatag.php' },
+		{ startIndex:18, type: 'number.php' },
+		{ startIndex:19, type: 'metatag.php' }
+	]}],
+
+	// php/html/php
+	[{
+	line: '<?php5+3?><abc><?=1?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: 'number.php' },
+		{ startIndex:6, type: 'delimiter.php' },
+		{ startIndex:7, type: 'number.php' },
+		{ startIndex:8, type: 'metatag.php' },
+		{ startIndex:10, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:11, type: htmlTokenTypes.getTag('abc') },
+		{ startIndex:14, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:15, type: 'metatag.php' },
+		{ startIndex:18, type: 'number.php' },
+		{ startIndex:19, type: 'metatag.php' }
+	]}],
+
+	// html/php/html
+	[{
+	line: '<abc><?php5+3?><abc>',
+	tokens: [
+		{ startIndex:0, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:1, type: htmlTokenTypes.getTag('abc') },
+		{ startIndex:4, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:5, type: 'metatag.php' },
+		{ startIndex:10, type: 'number.php' },
+		{ startIndex:11, type: 'delimiter.php' },
+		{ startIndex:12, type: 'number.php' },
+		{ startIndex:13, type: 'metatag.php' },
+		{ startIndex:15, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:16, type: htmlTokenTypes.getTag('abc') },
+		{ startIndex:19, type: htmlTokenTypes.DELIM_END }
+	]}],
+
+	// html/js/php/html
+	[{
+	line: '<abc><script>var i= 10;</script><?php5+3?><abc>',
+	tokens: [
+		{ startIndex:0, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:1, type: htmlTokenTypes.getTag('abc') },
+		{ startIndex:4, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:5, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:6, type: htmlTokenTypes.getTag('script') },
+		{ startIndex:12, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:13, type: '' },
+		{ startIndex:23, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:25, type: htmlTokenTypes.getTag('script') },
+		{ startIndex:31, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:32, type: 'metatag.php' },
+		{ startIndex:37, type: 'number.php' },
+		{ startIndex:38, type: 'delimiter.php' },
+		{ startIndex:39, type: 'number.php' },
+		{ startIndex:40, type: 'metatag.php' },
+		{ startIndex:42, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:43, type: htmlTokenTypes.getTag('abc') },
+		{ startIndex:46, type: htmlTokenTypes.DELIM_END }
+	]}],
+
+	// html/js/php/js/
+	[{
+	line: '<abc><script>var i= 10;</script><?php5+3?><script>var x= 15;</script>',
+	tokens: [
+		{ startIndex:0, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:1, type: htmlTokenTypes.getTag('abc') },
+		{ startIndex:4, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:5, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:6, type: htmlTokenTypes.getTag('script') },
+		{ startIndex:12, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:13, type: '' },
+		{ startIndex:23, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:25, type: htmlTokenTypes.getTag('script') },
+		{ startIndex:31, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:32, type: 'metatag.php' },
+		{ startIndex:37, type: 'number.php' },
+		{ startIndex:38, type: 'delimiter.php' },
+		{ startIndex:39, type: 'number.php' },
+		{ startIndex:40, type: 'metatag.php' },
+		{ startIndex:42, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:43, type: htmlTokenTypes.getTag('script') },
+		{ startIndex:49, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:50, type: '' },
+		{ startIndex:60, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:62, type: htmlTokenTypes.getTag('script') },
+		{ startIndex:68, type: htmlTokenTypes.DELIM_END }
+	]}],
+
+	// Multiline test
+	[{
+	line: '<html>',
+	tokens: [
+		{ startIndex:0, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:1, type: htmlTokenTypes.getTag('html') },
+		{ startIndex:5, type: htmlTokenTypes.DELIM_END }
+	]}, {
+	line: '<style><?="div"?>{ color:blue; }</style>',
+	tokens: [
+		{ startIndex:0, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:1, type: htmlTokenTypes.getTag('style') },
+		{ startIndex:6, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:7, type: 'metatag.php' },
+		{ startIndex:10, type: 'string.php' },
+		{ startIndex:15, type: 'metatag.php' },
+		{ startIndex:17, type: 'punctuation.curly.css' },
+		{ startIndex:18, type: '' },
+		{ startIndex:19, type: 'support.type.property-name.css' },
+		{ startIndex:25, type: 'support.property-value.css' },
+		{ startIndex:29, type: 'punctuation.css' },
+		{ startIndex:30, type: '' },
+		{ startIndex:31, type: 'punctuation.curly.css' },
+		{ startIndex:32, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:34, type: htmlTokenTypes.getTag('style') },
+		{ startIndex:39, type: htmlTokenTypes.DELIM_END }
+	]}],
+
+	// HTML (CSS (PHP)), HTML ( PHP, JS (PHP), PHP)
+	[{
+	line: '<html><style><?="div"?> { color:blue; }</style><!--<?="HTML Comment"?>--><script>var x = 3;/* <?="JS Comment"/*</script>*/?> */var y = 4;</script></html><? $x = 3;?>',
+	tokens: [
+		{ startIndex:0, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:1, type: htmlTokenTypes.getTag('html') },
+		{ startIndex:5, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:6, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:7, type: htmlTokenTypes.getTag('style') },
+		{ startIndex:12, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:13, type: 'metatag.php' },
+		{ startIndex:16, type: 'string.php' },
+		{ startIndex:21, type: 'metatag.php' },
+		{ startIndex:23, type: '' },
+		{ startIndex:24, type: 'punctuation.curly.css' },
+		{ startIndex:25, type: '' },
+		{ startIndex:26, type: 'support.type.property-name.css' },
+		{ startIndex:32, type: 'support.property-value.css' },
+		{ startIndex:36, type: 'punctuation.css' },
+		{ startIndex:37, type: '' },
+		{ startIndex:38, type: 'punctuation.curly.css' },
+		{ startIndex:39, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:41, type: htmlTokenTypes.getTag('style') },
+		{ startIndex:46, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:47, type: htmlTokenTypes.DELIM_COMMENT },
+		{ startIndex:51, type: 'metatag.php' },
+		{ startIndex:54, type: 'string.php' },
+		{ startIndex:68, type: 'metatag.php' },
+		{ startIndex:70, type: htmlTokenTypes.DELIM_COMMENT },
+		{ startIndex:73, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:74, type: htmlTokenTypes.getTag('script') },
+		{ startIndex:80, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:81, type: '' },
+		{ startIndex:94, type: 'metatag.php' },
+		{ startIndex:97, type: 'string.php' },
+		{ startIndex:109, type: 'comment.php' },
+		{ startIndex:122, type: 'metatag.php' },
+		{ startIndex:124, type: '' },
+		{ startIndex:137, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:139, type: htmlTokenTypes.getTag('script') },
+		{ startIndex:145, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:146, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:148, type: htmlTokenTypes.getTag('html') },
+		{ startIndex:152, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:153, type: 'metatag.php' },
+		{ startIndex:155, type: '' },
+		{ startIndex:156, type: 'variable.php' },
+		{ startIndex:158, type: '' },
+		{ startIndex:159, type: 'delimiter.php' },
+		{ startIndex:160, type: '' },
+		{ startIndex:161, type: 'number.php' },
+		{ startIndex:162, type: 'delimiter.php' },
+		{ startIndex:163, type: 'metatag.php' }
+	]}],
+
+	// PHP-tag detection
+	[{
+	line: '<!--c--><?',
+	tokens: [
+		{ startIndex:0, type: htmlTokenTypes.DELIM_COMMENT },
+		{ startIndex:8, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<script>//<?',
+	tokens: [
+		{ startIndex:0, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:1, type: htmlTokenTypes.getTag('script') },
+		{ startIndex:7, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:8, type: '' },
+		{ startIndex:10, type: 'metatag.php' }
+	]}],
+
+	[{
+	line: '<script>"<?php5+3?>"',
+	tokens: [
+		{ startIndex:0, type: htmlTokenTypes.DELIM_START },
+		{ startIndex:1, type: htmlTokenTypes.getTag('script') },
+		{ startIndex:7, type: htmlTokenTypes.DELIM_END },
+		{ startIndex:8, type: '' },
+		{ startIndex:9, type: 'metatag.php' },
+		{ startIndex:14, type: 'number.php' },
+		{ startIndex:15, type: 'delimiter.php' },
+		{ startIndex:16, type: 'number.php' },
+		{ startIndex:17, type: 'metatag.php' },
+		{ startIndex:19, type: '' }
+	]}],
+
+	[{
+	line: '<?php toString(); ?>',
+	tokens: [
+		{ startIndex:0, type: 'metatag.php' },
+		{ startIndex:5, type: '' },
+		{ startIndex:6, type: 'identifier.php' },
+		{ startIndex:14, type: 'delimiter.parenthesis.php' },
+		{ startIndex:16, type: 'delimiter.php' },
+		{ startIndex:17, type: '' },
+		{ startIndex:18, type: 'metatag.php' }
+	]}]
+]);

+ 11 - 4
test/testRunner.ts

@@ -19,11 +19,18 @@ export interface ITestItem {
 	tokens: IRelaxedToken[];
 }
 
-export function testTokenization(languageId:string, tests:ITestItem[][]): void {
-	suite(languageId + ' tokenization', () => {
+export function testTokenization(_language:string|string[], tests:ITestItem[][]): void {
+	let languages:string[];
+	if (typeof _language === 'string') {
+		languages = [_language];
+	} else {
+		languages = _language;
+	}
+	let mainLanguage = languages[0];
+	suite(mainLanguage + ' tokenization', () => {
 		test('', (done) => {
-			loadLanguage(languageId).then(() => {
-				runTests(languageId, tests);
+			_monaco.Promise.join(languages.map(l => loadLanguage(l))).then(() => {
+				runTests(mainLanguage, tests);
 				done();
 			}).then(null, done);
 		});

+ 2 - 0
tsconfig.json

@@ -28,6 +28,7 @@
     "src/monaco.contribution.ts",
     "src/objective-c.ts",
     "src/postiats.ts",
+    "src/php.ts",
     "src/powershell.ts",
     "src/python.ts",
     "src/r.ts",
@@ -51,6 +52,7 @@
     "test/markdown.test.ts",
     "test/mocha.d.ts",
     "test/objective-c.test.ts",
+    "test/php.test.ts",
     "test/postiats.test.ts",
     "test/powershell.test.ts",
     "test/python.test.ts",