src/vs/editor/standalone/common/monarch/monarchCompile.ts/compile
export function compile(languageId: string, json: IMonarchLanguage): monarchCommon.ILexer {
	if (!json || typeof (json) !== 'object') {
		throw new Error('Monarch: expecting a language definition object');
	}

	// Create our lexer
	let lexer: monarchCommon.ILexer = <monarchCommon.ILexer>{};
	lexer.languageId = languageId;
	lexer.includeLF = bool(json.includeLF, false);
	lexer.noThrow = false; // raise exceptions during compilation
	lexer.maxStack = 100;

	// Set standard fields: be defensive about types
	lexer.start = (typeof json.start === 'string' ? json.start : null);
	lexer.ignoreCase = bool(json.ignoreCase, false);
	lexer.unicode = bool(json.unicode, false);

	lexer.tokenPostfix = string(json.tokenPostfix, '.' + lexer.languageId);
	lexer.defaultToken = string(json.defaultToken, 'source');

	lexer.usesEmbedded = false; // becomes true if we find a nextEmbedded action

	// For calling compileAction later on
	let lexerMin: monarchCommon.ILexerMin = <any>json;
	lexerMin.languageId = languageId;
	lexerMin.includeLF = lexer.includeLF;
	lexerMin.ignoreCase = lexer.ignoreCase;
	lexerMin.unicode = lexer.unicode;
	lexerMin.noThrow = lexer.noThrow;
	lexerMin.usesEmbedded = lexer.usesEmbedded;
	lexerMin.stateNames = json.tokenizer;
	lexerMin.defaultToken = lexer.defaultToken;


	// Compile an array of rules into newrules where RegExp objects are created.
	function addRules(state: string, newrules: monarchCommon.IRule[], rules: any[]) {
		for (const rule of rules) {

			let include = rule.include;
			if (include) {
				if (typeof (include) !== 'string') {
					throw monarchCommon.createError(lexer, 'an \'include\' attribute must be a string at: ' + state);
				}
				if (include[0] === '@') {
					include = include.substr(1); // peel off starting @
				}
				if (!json.tokenizer[include]) {
					throw monarchCommon.createError(lexer, 'include target \'' + include + '\' is not defined at: ' + state);
				}
				addRules(state + '.' + include, newrules, json.tokenizer[include]);
			}
			else {
				const newrule = new Rule(state);

				// Set up new rule attributes
				if (Array.isArray(rule) && rule.length >= 1 && rule.length <= 3) {
					newrule.setRegex(lexerMin, rule[0]);
					if (rule.length >= 3) {
						if (typeof (rule[1]) === 'string') {
							newrule.setAction(lexerMin, { token: rule[1], next: rule[2] });
						}
						else if (typeof (rule[1]) === 'object') {
							const rule1 = rule[1];
							rule1.next = rule[2];
							newrule.setAction(lexerMin, rule1);
						}
						else {
							throw monarchCommon.createError(lexer, 'a next state as the last element of a rule can only be given if the action is either an object or a string, at: ' + state);
						}
					}
					else {
						newrule.setAction(lexerMin, rule[1]);
					}
				}
				else {
					if (!rule.regex) {
						throw monarchCommon.createError(lexer, 'a rule must either be an array, or an object with a \'regex\' or \'include\' field at: ' + state);
					}
					if (rule.name) {
						if (typeof rule.name === 'string') {
							newrule.name = rule.name;
						}
					}
					if (rule.matchOnlyAtStart) {
						newrule.matchOnlyAtLineStart = bool(rule.matchOnlyAtLineStart, false);
					}
					newrule.setRegex(lexerMin, rule.regex);
					newrule.setAction(lexerMin, rule.action);
				}

				newrules.push(newrule);
			}
		}
	}

	// compile the tokenizer rules
	if (!json.tokenizer || typeof (json.tokenizer) !== 'object') {
		throw monarchCommon.createError(lexer, 'a language definition must define the \'tokenizer\' attribute as an object');
	}

	lexer.tokenizer = <any>[];
	for (let key in json.tokenizer) {
		if (json.tokenizer.hasOwnProperty(key)) {
			if (!lexer.start) {
				lexer.start = key;
			}

			const rules = json.tokenizer[key];
			lexer.tokenizer[key] = new Array();
			addRules('tokenizer.' + key, lexer.tokenizer[key], rules);
		}
	}
	lexer.usesEmbedded = lexerMin.usesEmbedded;  // can be set during compileAction

	// Set simple brackets
	if (json.brackets) {
		if (!(Array.isArray(<any>json.brackets))) {
			throw monarchCommon.createError(lexer, 'the \'brackets\' attribute must be defined as an array');
		}
	}
	else {
		json.brackets = [
			{ open: '{', close: '}', token: 'delimiter.curly' },
			{ open: '[', close: ']', token: 'delimiter.square' },
			{ open: '(', close: ')', token: 'delimiter.parenthesis' },
			{ open: '<', close: '>', token: 'delimiter.angle' }];
	}
	let brackets: IMonarchLanguageBracket[] = [];
	for (let el of json.brackets) {
		let desc: any = el;
		if (desc && Array.isArray(desc) && desc.length === 3) {
			desc = { token: desc[2], open: desc[0], close: desc[1] };
		}
		if (desc.open === desc.close) {
			throw monarchCommon.createError(lexer, 'open and close brackets in a \'brackets\' attribute must be different: ' + desc.open +
				'\n hint: use the \'bracket\' attribute if matching on equal brackets is required.');
		}
		if (typeof desc.open === 'string' && typeof desc.token === 'string' && typeof desc.close === 'string') {
			brackets.push({
				token: desc.token + lexer.tokenPostfix,
				open: monarchCommon.fixCase(lexer, desc.open),
				close: monarchCommon.fixCase(lexer, desc.close)
			});
		}
		else {
			throw monarchCommon.createError(lexer, 'every element in the \'brackets\' array must be a \'{open,close,token}\' object or array');
		}
	}
	lexer.brackets = brackets;

	// Disable throw so the syntax highlighter goes, no matter what
	lexer.noThrow = true;
	return lexer;
}