function analyze( results, ast, src, comments, scope, depth ) {
	var nodes;
	var lines;
	var node;
	var type;
	var res;
	var str;
	var key;
	var v;
	var i;

	// Compute the number of characters:
	results.length += ast.end - ast.start;

	// Compute the number of physical lines:
	results.lines += ast.loc.end.line - ast.loc.start.line + 1;

	// Flatten the AST:
	node = ( ast.type !== 'Program' && ast.body ) ? ast.body : ast;
	nodes = flatten( node, scope, depth );

	// Calculate the number of source lines of codes (SLOC):
	results.sloc += sloc( nodes );

	// Calculate the number of logical lines of code (LLOC):
	results.lloc += lloc( nodes );

	// Isolate the source code containing the AST node:
	str = src.substring( node.start, node.end );

	// Split the source code string into separate lines:
	lines = str.split( RE_EOL );

	// Calculate the number of empty lines:
	results.empty += emptyLines( lines );

	// Calculate the amount of whitespace:
	results.whitespace += whitespace( str );

	// Set the scope level:
	results.scope = scope;

	// Set the nesting depth:
	results.depth = depth;

	// Tabulate AST node occurrences:
	results = tabulate( results, nodes );

	// Recursively analyze AST nodes:
	for ( i = 0; i < nodes.length; i++ ) {
		node = nodes[ i ];
		type = node.type;
		scope = node.__scope__; // eslint-disable-line no-underscore-dangle
		depth = node.__depth__; // eslint-disable-line no-underscore-dangle
		key = type2key( type );
		if ( contains( recurseNodes, type ) ) {
			// Initialize a new results object:
			res = resultsObject();

			// Guard against empty functions:
			if ( type === 'FunctionExpression' ) {
				str = src.substring( node.body.start+1, node.body.end-1 ); // everything inside `{}`

				if ( trim( str ) === '' ) {
					res.type = 'expression';
					res.name = ( node.id ) ? node.id.name : '(anonymous)';
					res.params = node.params.length;
					res.scope = scope;

					// Cache the sub-tree results:
					results[ key ].data.push( res );
					continue;
				}
			}
			// Compute sub-tree comment data:
			commentinfo( res, comments, node.start, node.end );

			// Increment the scope if we are analyzing a function AST:
			if (
				type === 'FunctionDeclaration' ||
				type === 'FunctionExpression'
			) {
				scope += 1;
			}
			// Analyze the AST sub-tree:
			res = analyze( res, node, src, comments, scope, depth );

			// Include results specific to particular AST nodes:
			if ( type === 'SwitchStatement' ) {
				res.case = node.cases.length;
			} else if ( type === 'FunctionDeclaration' ) {
				res.type = 'declaration';
				res.name = node.id.name;
				res.params = node.params.length;
				res.scope -= 1; // while contents are in a nested scope, declaration is in parent scope
			} else if ( type === 'FunctionExpression' ) {
				res.type = 'expression';
				res.name = ( node.id ) ? node.id.name : '(anonymous)';
				res.params = node.params.length;
				res.scope -= 1; // while contents are in a nested scope, declaration is in parent scope
			}
			// Cache the sub-tree results:
			results[ key ].data.push( res );
		} else if ( type === 'VariableDeclaration' ) {
			// Extract variable declaration data:
			v = varinfo( node );

			// Append the results to the existing variable declaration data:
			results[ key ].data = results[ key ].data.concat( v );
		} else if ( type === 'Literal' ) {
			// Extract literal data:
			v = literalinfo( node );

			// Append the results to the existing literal data:
			results[ key ].data.push( v );
		}
	}
	// Return the results:
	return results;
}
Analyzes an AST to generate a summary.

@private
@param {Object} results - output results object
@param {Node} ast - AST node
@param {string} src - source code
@param {ObjectArray} comments - source code comments
@param {NonNegativeInteger} scope - scope level
@param {NonNegativeInteger} depth - nesting depth
@returns {Object} results