function read_sequence(parser, done, location) {
	/** @type {AST.Text} */
	let current_chunk = {
		start: parser.index,
		end: -1,
		type: 'Text',
		raw: '',
		data: ''
	};

	/** @type {Array<AST.Text | AST.ExpressionTag>} */
	const chunks = [];

	/** @param {number} end */
	function flush(end) {
		if (current_chunk.raw) {
			current_chunk.data = decode_character_references(current_chunk.raw, true);
			current_chunk.end = end;
			chunks.push(current_chunk);
		}
	}

	while (parser.index < parser.template.length) {
		const index = parser.index;

		if (done()) {
			flush(parser.index);
			return chunks;
		} else if (parser.eat('{')) {
			if (parser.match('#')) {
				const index = parser.index - 1;
				parser.eat('#');
				const name = parser.read_until(/[^a-z]/);
				e.block_invalid_placement(index, name, location);
			} else if (parser.match('@')) {
				const index = parser.index - 1;
				parser.eat('@');
				const name = parser.read_until(/[^a-z]/);
				e.tag_invalid_placement(index, name, location);
			}

			flush(parser.index - 1);

			parser.allow_whitespace();
			const expression = read_expression(parser);
			parser.allow_whitespace();
			parser.eat('}', true);

			/** @type {AST.ExpressionTag} */
			const chunk = {
				type: 'ExpressionTag',
				start: index,
				end: parser.index,
				expression,
				metadata: {
					expression: create_expression_metadata()
				}
			};

			chunks.push(chunk);

			current_chunk = {
				start: parser.index,
				end: -1,
				type: 'Text',
				raw: '',
				data: ''
			};
		} else {
			current_chunk.raw += parser.template[parser.index++];
		}
	}

	if (parser.loose) {
		return chunks;
	} else {
		e.unexpected_eof(parser.template.length);
	}
}
@param {Parser} parser
@param {() => boolean} done
@param {string} location
@returns {any[]}