string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback _readCallback)
{
	Json::Value output(Json::objectValue);
	Json::Value errors(Json::arrayValue);
	CompilerStack::ReadFileCallback readCallback;
	if (_readCallback)
	{
		readCallback = [=](string const& _path)
		{
			char* contents_c = nullptr;
			char* error_c = nullptr;
			_readCallback(_path.c_str(), &contents_c, &error_c);
			CompilerStack::ReadFileResult result;
			result.success = true;
			if (!contents_c && !error_c)
			{
				result.success = false;
				result.contentsOrErrorMesage = "File not found.";
			}
			if (contents_c)
			{
				result.success = true;
				result.contentsOrErrorMesage = string(contents_c);
				free(contents_c);
			}
			if (error_c)
			{
				result.success = false;
				result.contentsOrErrorMesage = string(error_c);
				free(error_c);
			}
			return result;
		};
	}
	CompilerStack compiler(readCallback);
	auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return compiler.scanner(_sourceName); };
	bool success = false;
	try
	{
		compiler.addSources(_sources);
		bool succ = compiler.compile(_optimize);
		for (auto const& error: compiler.errors())
		{
			auto err = dynamic_pointer_cast<Error const>(error);
			errors.append(formatError(
				*error,
				(err->type() == Error::Type::Warning) ? "Warning" : "Error",
				scannerFromSourceName
			));
		}
		success = succ; // keep success false on exception
	}
	catch (Error const& error)
	{
		errors.append(formatError(error, error.typeName(), scannerFromSourceName));
	}
	catch (CompilerError const& exception)
	{
		errors.append(formatError(exception, "Compiler error", scannerFromSourceName));
	}
	catch (InternalCompilerError const& exception)
	{
		errors.append(formatError(exception, "Internal compiler error", scannerFromSourceName));
	}
	catch (Exception const& exception)
	{
		errors.append("Exception during compilation: " + boost::diagnostic_information(exception));
	}
	catch (...)
	{
		errors.append("Unknown exception during compilation.");
	}

	if (errors.size() > 0)
		output["errors"] = errors;

	if (success)
	{
		try
		{
			output["contracts"] = Json::Value(Json::objectValue);
			for (string const& contractName: compiler.contractNames())
			{
				Json::Value contractData(Json::objectValue);
				contractData["interface"] = compiler.interface(contractName);
				contractData["bytecode"] = compiler.object(contractName).toHex();
				contractData["runtimeBytecode"] = compiler.runtimeObject(contractName).toHex();
				contractData["opcodes"] = solidity::disassemble(compiler.object(contractName).bytecode);
				contractData["functionHashes"] = functionHashes(compiler.contractDefinition(contractName));
				contractData["gasEstimates"] = estimateGas(compiler, contractName);
				auto sourceMap = compiler.sourceMapping(contractName);
				contractData["srcmap"] = sourceMap ? *sourceMap : "";
				auto runtimeSourceMap = compiler.runtimeSourceMapping(contractName);
				contractData["srcmapRuntime"] = runtimeSourceMap ? *runtimeSourceMap : "";
				ostringstream unused;
				contractData["assembly"] = compiler.streamAssembly(unused, contractName, _sources, true);
				output["contracts"][contractName] = contractData;
			}
		}
		catch (...)
		{
			output["errors"].append("Unknown exception while generating contract data output.");
		}

		try
		{
			// Do not taint the internal error list
			ErrorList formalErrors;
			if (compiler.prepareFormalAnalysis(&formalErrors))
				output["formal"]["why3"] = compiler.formalTranslation();
			if (!formalErrors.empty())
			{
				Json::Value errors(Json::arrayValue);
				for (auto const& error: formalErrors)
					errors.append(formatError(
						*error,
						(error->type() == Error::Type::Warning) ? "Warning" : "Error",
						scannerFromSourceName
					));
				output["formal"]["errors"] = errors;
			}
		}
		catch (...)
		{
			output["errors"].append("Unknown exception while generating formal method output.");
		}

		try
		{
			// Indices into this array are used to abbreviate source names in source locations.
			output["sourceList"] = Json::Value(Json::arrayValue);
			for (auto const& source: compiler.sourceNames())
				output["sourceList"].append(source);
			output["sources"] = Json::Value(Json::objectValue);
			for (auto const& source: compiler.sourceNames())
				output["sources"][source]["AST"] = ASTJsonConverter(compiler.ast(source), compiler.sourceIndices()).json();
		}
		catch (...)
		{
			output["errors"].append("Unknown exception while generating source name output.");
		}
	}

	try
	{
		return Json::FastWriter().write(output);
	}
	catch (...)
	{
		return "{\"errors\":[\"Unknown error while generating JSON.\"]}";
	}
}