<commit_before>/*-------------------------------------------------------------------------
 * Vulkan CTS Framework
 * --------------------
 *
 * Copyright (c) 2015 Google Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice(s) and this permission notice shall be
 * included in all copies or substantial portions of the Materials.
 *
 * The Materials are Confidential Information as defined by the
 * Khronos Membership Agreement until designated non-confidential by
 * Khronos, at which point this condition clause shall be removed.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 *
 *//*!
 * \file
 * \brief GLSL to SPIR-V.
 *//*--------------------------------------------------------------------*/

#include "vkGlslToSpirV.hpp"
#include "deArrayUtil.hpp"
#include "deMemory.h"
#include "deClock.h"
#include "qpDebugOut.h"

#if defined(DEQP_HAVE_GLSLANG)
#	include "deSingleton.h"
#	include "deMutex.hpp"

#	include "SPIRV/GlslangToSpv.h"
#	include "SPIRV/disassemble.h"
#	include "SPIRV/doc.h"
#	include "glslang/Include/InfoSink.h"
#	include "glslang/Include/ShHandle.h"
#	include "glslang/MachineIndependent/localintermediate.h"
#	include "glslang/Public/ShaderLang.h"

#endif

namespace vk
{

using std::string;
using std::vector;

#if defined(DEQP_HAVE_GLSLANG)

namespace
{

EShLanguage getGlslangStage (glu::ShaderType type)
{
	static const EShLanguage stageMap[] =
	{
		EShLangVertex,
		EShLangFragment,
		EShLangGeometry,
		EShLangTessControl,
		EShLangTessEvaluation,
		EShLangCompute,
	};
	return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(stageMap, type);
}

static volatile deSingletonState	s_glslangInitState	= DE_SINGLETON_STATE_NOT_INITIALIZED;
static de::Mutex					s_glslangLock;

void initGlslang (void*)
{
	// Main compiler
	glslang::InitializeProcess();

	// SPIR-V disassembly
	spv::Parameterize();
}

void prepareGlslang (void)
{
	deInitSingleton(&s_glslangInitState, initGlslang, DE_NULL);
}

// \todo [2015-06-19 pyry] Specialize these per GLSL version

// Fail compilation if more members are added to TLimits or TBuiltInResource
struct LimitsSizeHelper_s			{ bool m0, m1, m2, m3, m4, m5, m6, m7, m8; };
struct BuiltInResourceSizeHelper_s	{ int m[83]; LimitsSizeHelper_s l; };

DE_STATIC_ASSERT(sizeof(TLimits)			== sizeof(LimitsSizeHelper_s));
DE_STATIC_ASSERT(sizeof(TBuiltInResource)	== sizeof(BuiltInResourceSizeHelper_s));

void getDefaultLimits (TLimits* limits)
{
	limits->nonInductiveForLoops					= true;
	limits->whileLoops								= true;
	limits->doWhileLoops							= true;
	limits->generalUniformIndexing					= true;
	limits->generalAttributeMatrixVectorIndexing	= true;
	limits->generalVaryingIndexing					= true;
	limits->generalSamplerIndexing					= true;
	limits->generalVariableIndexing					= true;
	limits->generalConstantMatrixVectorIndexing		= true;
}

void getDefaultBuiltInResources (TBuiltInResource* builtin)
{
	getDefaultLimits(&builtin->limits);

	builtin->maxLights									= 32;
	builtin->maxClipPlanes								= 6;
	builtin->maxTextureUnits							= 32;
	builtin->maxTextureCoords							= 32;
	builtin->maxVertexAttribs							= 64;
	builtin->maxVertexUniformComponents					= 4096;
	builtin->maxVaryingFloats							= 64;
	builtin->maxVertexTextureImageUnits					= 32;
	builtin->maxCombinedTextureImageUnits				= 80;
	builtin->maxTextureImageUnits						= 32;
	builtin->maxFragmentUniformComponents				= 4096;
	builtin->maxDrawBuffers								= 32;
	builtin->maxVertexUniformVectors					= 128;
	builtin->maxVaryingVectors							= 8;
	builtin->maxFragmentUniformVectors					= 16;
	builtin->maxVertexOutputVectors						= 16;
	builtin->maxFragmentInputVectors					= 15;
	builtin->minProgramTexelOffset						= -8;
	builtin->maxProgramTexelOffset						= 7;
	builtin->maxClipDistances							= 8;
	builtin->maxComputeWorkGroupCountX					= 65535;
	builtin->maxComputeWorkGroupCountY					= 65535;
	builtin->maxComputeWorkGroupCountZ					= 65535;
	builtin->maxComputeWorkGroupSizeX					= 1024;
	builtin->maxComputeWorkGroupSizeY					= 1024;
	builtin->maxComputeWorkGroupSizeZ					= 64;
	builtin->maxComputeUniformComponents				= 1024;
	builtin->maxComputeTextureImageUnits				= 16;
	builtin->maxComputeImageUniforms					= 8;
	builtin->maxComputeAtomicCounters					= 8;
	builtin->maxComputeAtomicCounterBuffers				= 1;
	builtin->maxVaryingComponents						= 60;
	builtin->maxVertexOutputComponents					= 64;
	builtin->maxGeometryInputComponents					= 64;
	builtin->maxGeometryOutputComponents				= 128;
	builtin->maxFragmentInputComponents					= 128;
	builtin->maxImageUnits								= 8;
	builtin->maxCombinedImageUnitsAndFragmentOutputs	= 8;
	builtin->maxCombinedShaderOutputResources			= 8;
	builtin->maxImageSamples							= 0;
	builtin->maxVertexImageUniforms						= 0;
	builtin->maxTessControlImageUniforms				= 0;
	builtin->maxTessEvaluationImageUniforms				= 0;
	builtin->maxGeometryImageUniforms					= 0;
	builtin->maxFragmentImageUniforms					= 8;
	builtin->maxCombinedImageUniforms					= 8;
	builtin->maxGeometryTextureImageUnits				= 16;
	builtin->maxGeometryOutputVertices					= 256;
	builtin->maxGeometryTotalOutputComponents			= 1024;
	builtin->maxGeometryUniformComponents				= 1024;
	builtin->maxGeometryVaryingComponents				= 64;
	builtin->maxTessControlInputComponents				= 128;
	builtin->maxTessControlOutputComponents				= 128;
	builtin->maxTessControlTextureImageUnits			= 16;
	builtin->maxTessControlUniformComponents			= 1024;
	builtin->maxTessControlTotalOutputComponents		= 4096;
	builtin->maxTessEvaluationInputComponents			= 128;
	builtin->maxTessEvaluationOutputComponents			= 128;
	builtin->maxTessEvaluationTextureImageUnits			= 16;
	builtin->maxTessEvaluationUniformComponents			= 1024;
	builtin->maxTessPatchComponents						= 120;
	builtin->maxPatchVertices							= 32;
	builtin->maxTessGenLevel							= 64;
	builtin->maxViewports								= 16;
	builtin->maxVertexAtomicCounters					= 0;
	builtin->maxTessControlAtomicCounters				= 0;
	builtin->maxTessEvaluationAtomicCounters			= 0;
	builtin->maxGeometryAtomicCounters					= 0;
	builtin->maxFragmentAtomicCounters					= 8;
	builtin->maxCombinedAtomicCounters					= 8;
	builtin->maxAtomicCounterBindings					= 1;
	builtin->maxVertexAtomicCounterBuffers				= 0;
	builtin->maxTessControlAtomicCounterBuffers			= 0;
	builtin->maxTessEvaluationAtomicCounterBuffers		= 0;
	builtin->maxGeometryAtomicCounterBuffers			= 0;
	builtin->maxFragmentAtomicCounterBuffers			= 1;
	builtin->maxCombinedAtomicCounterBuffers			= 1;
	builtin->maxAtomicCounterBufferSize					= 16384;
	builtin->maxTransformFeedbackBuffers				= 4;
	builtin->maxTransformFeedbackInterleavedComponents	= 64;
	builtin->maxCullDistances							= 8;
	builtin->maxCombinedClipAndCullDistances			= 8;
	builtin->maxSamples									= 4;
};

} // anonymous

void glslToSpirV (const glu::ProgramSources& program, std::vector<deUint8>* dst, glu::ShaderProgramInfo* buildInfo)
{
	TBuiltInResource	builtinRes;

	prepareGlslang();
	getDefaultBuiltInResources(&builtinRes);

	// \note Compiles only first found shader
	for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
	{
		if (!program.sources[shaderType].empty())
		{
			const de::ScopedLock	compileLock			(s_glslangLock);
			const std::string&		srcText				= program.sources[shaderType][0];
			const char*				srcPtrs[]			= { srcText.c_str() };
			const int				srcLengths[]		= { (int)srcText.size() };
			vector<deUint32>		spvBlob;
			const EShLanguage		shaderStage			= getGlslangStage(glu::ShaderType(shaderType));
			glslang::TShader		shader				(shaderStage);
			glslang::TProgram		program;

			shader.setStrings(srcPtrs, DE_LENGTH_OF_ARRAY(srcPtrs));
			program.addShader(&shader);

			{
				const deUint64	compileStartTime	= deGetMicroseconds();
				const int		compileRes			= shader.parse(&builtinRes, 110, false, EShMsgSpvRules);
				glu::ShaderInfo	shaderBuildInfo;

				shaderBuildInfo.type			= (glu::ShaderType)shaderType;
				shaderBuildInfo.source			= srcText;
				shaderBuildInfo.infoLog			= shader.getInfoLog(); // \todo [2015-07-13 pyry] Include debug log?
				shaderBuildInfo.compileTimeUs	= deGetMicroseconds()-compileStartTime;
				shaderBuildInfo.compileOk		= (compileRes != 0);

				buildInfo->shaders.push_back(shaderBuildInfo);

				if (compileRes == 0)
					TCU_FAIL("Failed to compile shader");
			}

			{
				const deUint64	linkStartTime	= deGetMicroseconds();
				const int		linkRes			= program.link(EShMsgDefault);

				buildInfo->program.infoLog		= program.getInfoLog(); // \todo [2015-11-05 scygan] Include debug log?
				buildInfo->program.linkOk		= (linkRes != 0);
				buildInfo->program.linkTimeUs	= deGetMicroseconds()-linkStartTime;

				if (linkRes == 0)
					TCU_FAIL("Failed to link shader");
			}

			{
				const glslang::TIntermediate* const	intermediate	= program.getIntermediate(shaderStage);
				glslang::GlslangToSpv(*intermediate, spvBlob);
			}

			dst->resize(spvBlob.size() * sizeof(deUint32));
#if (DE_ENDIANNESS == DE_LITTLE_ENDIAN)
			deMemcpy(&(*dst)[0], &spvBlob[0], dst->size());
#else
#	error "Big-endian not supported"
#endif

			return;
		}
	}

	TCU_THROW(InternalError, "Can't compile empty program");
}

void disassembleSpirV (size_t binarySize, const deUint8* binary, std::ostream* dst)
{
	std::vector<deUint32>	binForDisasm	(binarySize/4);

	DE_ASSERT(binarySize%4 == 0);

#if (DE_ENDIANNESS == DE_LITTLE_ENDIAN)
	deMemcpy(&binForDisasm[0], binary, binarySize);
#else
#	error "Big-endian not supported"
#endif

	spv::Disassemble(*dst, binForDisasm);
}

#else // defined(DEQP_HAVE_GLSLANG)

void glslToSpirV (const glu::ProgramSources&, std::vector<deUint8>*, glu::ShaderProgramInfo*)
{
	TCU_THROW(NotSupportedError, "GLSL to SPIR-V compilation not supported (DEQP_HAVE_GLSLANG not defined)");
}

void disassembleSpirV (size_t, const deUint8*, std::ostream*)
{
	TCU_THROW(NotSupportedError, "SPIR-V disassembling not supported (DEQP_HAVE_GLSLANG not defined)");
}

#endif

} // vk
<commit_msg>Set glslang SPV/Vulkan flags<commit_after>/*-------------------------------------------------------------------------
 * Vulkan CTS Framework
 * --------------------
 *
 * Copyright (c) 2015 Google Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice(s) and this permission notice shall be
 * included in all copies or substantial portions of the Materials.
 *
 * The Materials are Confidential Information as defined by the
 * Khronos Membership Agreement until designated non-confidential by
 * Khronos, at which point this condition clause shall be removed.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 *
 *//*!
 * \file
 * \brief GLSL to SPIR-V.
 *//*--------------------------------------------------------------------*/

#include "vkGlslToSpirV.hpp"
#include "deArrayUtil.hpp"
#include "deMemory.h"
#include "deClock.h"
#include "qpDebugOut.h"

#if defined(DEQP_HAVE_GLSLANG)
#	include "deSingleton.h"
#	include "deMutex.hpp"

#	include "SPIRV/GlslangToSpv.h"
#	include "SPIRV/disassemble.h"
#	include "SPIRV/doc.h"
#	include "glslang/Include/InfoSink.h"
#	include "glslang/Include/ShHandle.h"
#	include "glslang/MachineIndependent/localintermediate.h"
#	include "glslang/Public/ShaderLang.h"

#endif

namespace vk
{

using std::string;
using std::vector;

#if defined(DEQP_HAVE_GLSLANG)

namespace
{

EShLanguage getGlslangStage (glu::ShaderType type)
{
	static const EShLanguage stageMap[] =
	{
		EShLangVertex,
		EShLangFragment,
		EShLangGeometry,
		EShLangTessControl,
		EShLangTessEvaluation,
		EShLangCompute,
	};
	return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(stageMap, type);
}

static volatile deSingletonState	s_glslangInitState	= DE_SINGLETON_STATE_NOT_INITIALIZED;
static de::Mutex					s_glslangLock;

void initGlslang (void*)
{
	// Main compiler
	glslang::InitializeProcess();

	// SPIR-V disassembly
	spv::Parameterize();
}

void prepareGlslang (void)
{
	deInitSingleton(&s_glslangInitState, initGlslang, DE_NULL);
}

// \todo [2015-06-19 pyry] Specialize these per GLSL version

// Fail compilation if more members are added to TLimits or TBuiltInResource
struct LimitsSizeHelper_s			{ bool m0, m1, m2, m3, m4, m5, m6, m7, m8; };
struct BuiltInResourceSizeHelper_s	{ int m[83]; LimitsSizeHelper_s l; };

DE_STATIC_ASSERT(sizeof(TLimits)			== sizeof(LimitsSizeHelper_s));
DE_STATIC_ASSERT(sizeof(TBuiltInResource)	== sizeof(BuiltInResourceSizeHelper_s));

void getDefaultLimits (TLimits* limits)
{
	limits->nonInductiveForLoops					= true;
	limits->whileLoops								= true;
	limits->doWhileLoops							= true;
	limits->generalUniformIndexing					= true;
	limits->generalAttributeMatrixVectorIndexing	= true;
	limits->generalVaryingIndexing					= true;
	limits->generalSamplerIndexing					= true;
	limits->generalVariableIndexing					= true;
	limits->generalConstantMatrixVectorIndexing		= true;
}

void getDefaultBuiltInResources (TBuiltInResource* builtin)
{
	getDefaultLimits(&builtin->limits);

	builtin->maxLights									= 32;
	builtin->maxClipPlanes								= 6;
	builtin->maxTextureUnits							= 32;
	builtin->maxTextureCoords							= 32;
	builtin->maxVertexAttribs							= 64;
	builtin->maxVertexUniformComponents					= 4096;
	builtin->maxVaryingFloats							= 64;
	builtin->maxVertexTextureImageUnits					= 32;
	builtin->maxCombinedTextureImageUnits				= 80;
	builtin->maxTextureImageUnits						= 32;
	builtin->maxFragmentUniformComponents				= 4096;
	builtin->maxDrawBuffers								= 32;
	builtin->maxVertexUniformVectors					= 128;
	builtin->maxVaryingVectors							= 8;
	builtin->maxFragmentUniformVectors					= 16;
	builtin->maxVertexOutputVectors						= 16;
	builtin->maxFragmentInputVectors					= 15;
	builtin->minProgramTexelOffset						= -8;
	builtin->maxProgramTexelOffset						= 7;
	builtin->maxClipDistances							= 8;
	builtin->maxComputeWorkGroupCountX					= 65535;
	builtin->maxComputeWorkGroupCountY					= 65535;
	builtin->maxComputeWorkGroupCountZ					= 65535;
	builtin->maxComputeWorkGroupSizeX					= 1024;
	builtin->maxComputeWorkGroupSizeY					= 1024;
	builtin->maxComputeWorkGroupSizeZ					= 64;
	builtin->maxComputeUniformComponents				= 1024;
	builtin->maxComputeTextureImageUnits				= 16;
	builtin->maxComputeImageUniforms					= 8;
	builtin->maxComputeAtomicCounters					= 8;
	builtin->maxComputeAtomicCounterBuffers				= 1;
	builtin->maxVaryingComponents						= 60;
	builtin->maxVertexOutputComponents					= 64;
	builtin->maxGeometryInputComponents					= 64;
	builtin->maxGeometryOutputComponents				= 128;
	builtin->maxFragmentInputComponents					= 128;
	builtin->maxImageUnits								= 8;
	builtin->maxCombinedImageUnitsAndFragmentOutputs	= 8;
	builtin->maxCombinedShaderOutputResources			= 8;
	builtin->maxImageSamples							= 0;
	builtin->maxVertexImageUniforms						= 0;
	builtin->maxTessControlImageUniforms				= 0;
	builtin->maxTessEvaluationImageUniforms				= 0;
	builtin->maxGeometryImageUniforms					= 0;
	builtin->maxFragmentImageUniforms					= 8;
	builtin->maxCombinedImageUniforms					= 8;
	builtin->maxGeometryTextureImageUnits				= 16;
	builtin->maxGeometryOutputVertices					= 256;
	builtin->maxGeometryTotalOutputComponents			= 1024;
	builtin->maxGeometryUniformComponents				= 1024;
	builtin->maxGeometryVaryingComponents				= 64;
	builtin->maxTessControlInputComponents				= 128;
	builtin->maxTessControlOutputComponents				= 128;
	builtin->maxTessControlTextureImageUnits			= 16;
	builtin->maxTessControlUniformComponents			= 1024;
	builtin->maxTessControlTotalOutputComponents		= 4096;
	builtin->maxTessEvaluationInputComponents			= 128;
	builtin->maxTessEvaluationOutputComponents			= 128;
	builtin->maxTessEvaluationTextureImageUnits			= 16;
	builtin->maxTessEvaluationUniformComponents			= 1024;
	builtin->maxTessPatchComponents						= 120;
	builtin->maxPatchVertices							= 32;
	builtin->maxTessGenLevel							= 64;
	builtin->maxViewports								= 16;
	builtin->maxVertexAtomicCounters					= 0;
	builtin->maxTessControlAtomicCounters				= 0;
	builtin->maxTessEvaluationAtomicCounters			= 0;
	builtin->maxGeometryAtomicCounters					= 0;
	builtin->maxFragmentAtomicCounters					= 8;
	builtin->maxCombinedAtomicCounters					= 8;
	builtin->maxAtomicCounterBindings					= 1;
	builtin->maxVertexAtomicCounterBuffers				= 0;
	builtin->maxTessControlAtomicCounterBuffers			= 0;
	builtin->maxTessEvaluationAtomicCounterBuffers		= 0;
	builtin->maxGeometryAtomicCounterBuffers			= 0;
	builtin->maxFragmentAtomicCounterBuffers			= 1;
	builtin->maxCombinedAtomicCounterBuffers			= 1;
	builtin->maxAtomicCounterBufferSize					= 16384;
	builtin->maxTransformFeedbackBuffers				= 4;
	builtin->maxTransformFeedbackInterleavedComponents	= 64;
	builtin->maxCullDistances							= 8;
	builtin->maxCombinedClipAndCullDistances			= 8;
	builtin->maxSamples									= 4;
};

} // anonymous

void glslToSpirV (const glu::ProgramSources& program, std::vector<deUint8>* dst, glu::ShaderProgramInfo* buildInfo)
{
	TBuiltInResource	builtinRes;

	prepareGlslang();
	getDefaultBuiltInResources(&builtinRes);

	// \note Compiles only first found shader
	for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
	{
		if (!program.sources[shaderType].empty())
		{
			const de::ScopedLock	compileLock			(s_glslangLock);
			const std::string&		srcText				= program.sources[shaderType][0];
			const char*				srcPtrs[]			= { srcText.c_str() };
			const int				srcLengths[]		= { (int)srcText.size() };
			vector<deUint32>		spvBlob;
			const EShLanguage		shaderStage			= getGlslangStage(glu::ShaderType(shaderType));
			glslang::TShader		shader				(shaderStage);
			glslang::TProgram		program;

			shader.setStrings(srcPtrs, DE_LENGTH_OF_ARRAY(srcPtrs));
			program.addShader(&shader);

			{
				const deUint64	compileStartTime	= deGetMicroseconds();
				const int		compileRes			= shader.parse(&builtinRes, 110, false, (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules));
				glu::ShaderInfo	shaderBuildInfo;

				shaderBuildInfo.type			= (glu::ShaderType)shaderType;
				shaderBuildInfo.source			= srcText;
				shaderBuildInfo.infoLog			= shader.getInfoLog(); // \todo [2015-07-13 pyry] Include debug log?
				shaderBuildInfo.compileTimeUs	= deGetMicroseconds()-compileStartTime;
				shaderBuildInfo.compileOk		= (compileRes != 0);

				buildInfo->shaders.push_back(shaderBuildInfo);

				if (compileRes == 0)
					TCU_FAIL("Failed to compile shader");
			}

			{
				const deUint64	linkStartTime	= deGetMicroseconds();
				const int		linkRes			= program.link((EShMessages)(EShMsgSpvRules | EShMsgVulkanRules));

				buildInfo->program.infoLog		= program.getInfoLog(); // \todo [2015-11-05 scygan] Include debug log?
				buildInfo->program.linkOk		= (linkRes != 0);
				buildInfo->program.linkTimeUs	= deGetMicroseconds()-linkStartTime;

				if (linkRes == 0)
					TCU_FAIL("Failed to link shader");
			}

			{
				const glslang::TIntermediate* const	intermediate	= program.getIntermediate(shaderStage);
				glslang::GlslangToSpv(*intermediate, spvBlob);
			}

			dst->resize(spvBlob.size() * sizeof(deUint32));
#if (DE_ENDIANNESS == DE_LITTLE_ENDIAN)
			deMemcpy(&(*dst)[0], &spvBlob[0], dst->size());
#else
#	error "Big-endian not supported"
#endif

			return;
		}
	}

	TCU_THROW(InternalError, "Can't compile empty program");
}

void disassembleSpirV (size_t binarySize, const deUint8* binary, std::ostream* dst)
{
	std::vector<deUint32>	binForDisasm	(binarySize/4);

	DE_ASSERT(binarySize%4 == 0);

#if (DE_ENDIANNESS == DE_LITTLE_ENDIAN)
	deMemcpy(&binForDisasm[0], binary, binarySize);
#else
#	error "Big-endian not supported"
#endif

	spv::Disassemble(*dst, binForDisasm);
}

#else // defined(DEQP_HAVE_GLSLANG)

void glslToSpirV (const glu::ProgramSources&, std::vector<deUint8>*, glu::ShaderProgramInfo*)
{
	TCU_THROW(NotSupportedError, "GLSL to SPIR-V compilation not supported (DEQP_HAVE_GLSLANG not defined)");
}

void disassembleSpirV (size_t, const deUint8*, std::ostream*)
{
	TCU_THROW(NotSupportedError, "SPIR-V disassembling not supported (DEQP_HAVE_GLSLANG not defined)");
}

#endif

} // vk
<|endoftext|>


<commit_before>#ifndef STAN_MATH_PRIM_META_VECTORBUILDER_HPP
#define STAN_MATH_PRIM_META_VECTORBUILDER_HPP

#include <stan/math/prim/meta/VectorBuilderHelper.hpp>
#include <stan/math/prim/meta/is_vector.hpp>

namespace stan {

/** \ingroup type_trait
 *
 *  VectorBuilder allocates type T1 values to be used as
 *  intermediate values. There are 2 template parameters:
 *  - used: boolean variable indicating whether this instance
 *      is used. If this is false, there is no storage allocated
 *      and operator[] throws.
 *  - is_vec: boolean variable indicating whether this instance
 *      should allocate a vector, if it is used. If this is false,
 *      the instance will only allocate a single double value.
 *      If this is true, it will allocate the number requested.
 *      Note that this is calculated based on template parameters
 *      T2 through T7.
 *
 *  These values are mutable.
 */
template <bool used, typename T1, typename... Args>
class VectorBuilder {
 private:
  using helper = VectorBuilderHelper<T1, used, is_vector<Args>::value...>;

 public:
  using type = typename helper::type;
  helper a;

  explicit VectorBuilder(size_t n) : a(n) {}

  T1& operator[](size_t i) { return a[i]; }

  inline type data() { return a.data(); }
};

}  // namespace stan
#endif
<commit_msg>Fixed `is_vector` check in `VectorBuilder` (Issue #2101)<commit_after>#ifndef STAN_MATH_PRIM_META_VECTORBUILDER_HPP
#define STAN_MATH_PRIM_META_VECTORBUILDER_HPP

#include <stan/math/prim/meta/VectorBuilderHelper.hpp>
#include <stan/math/prim/meta/disjunction.hpp>
#include <stan/math/prim/meta/is_vector.hpp>

namespace stan {

/** \ingroup type_trait
 *
 *  VectorBuilder allocates type T1 values to be used as
 *  intermediate values. There are 2 template parameters:
 *  - used: boolean variable indicating whether this instance
 *      is used. If this is false, there is no storage allocated
 *      and operator[] throws.
 *  - is_vec: boolean variable indicating whether this instance
 *      should allocate a vector, if it is used. If this is false,
 *      the instance will only allocate a single double value.
 *      If this is true, it will allocate the number requested.
 *      Note that this is calculated based on template parameters
 *      T2 through T7.
 *
 *  These values are mutable.
 */
template <bool used, typename T1, typename... Args>
class VectorBuilder {
 private:
  using helper = VectorBuilderHelper<T1, used, math::disjunction<is_vector<Args>...>::value>;

 public:
  using type = typename helper::type;
  helper a;

  explicit VectorBuilder(size_t n) : a(n) {}

  T1& operator[](size_t i) { return a[i]; }

  inline type data() { return a.data(); }
};

}  // namespace stan
#endif
<|endoftext|>


<commit_before>#include "Carmichael.h"
#include "SquareFree.h"
#include "PollardRho.h"

namespace Math {
  namespace Fac {
    bool isCarmichael(const mpz_t n) {
      if (!isSquareFree(n)) return false;

      vector<mpz_t*> facs;
      pollardRhoFactor(n, facs);

      mpz_t tmp, tmp2;
      mpz_inits(tmp, tmp2, NULL);
      mpz_sub_ui(tmp, n, 1);

      for (size_t i = 0; i < facs.size(); i++) {
        mpz_sub_ui(tmp2, *facs[i], 1);
        mpz_mod(tmp2, tmp, tmp2);
        if (mpz_cmp_ui(tmp2, 0) != 0) {
          mpz_clears(tmp, tmp2, NULL);
          for (size_t j = 0; j < facs.size(); j++) {
            mpz_clear(*facs[j]);
            delete[] facs[j];
          }
          return false;
        }

        mpz_clear(*facs[i]);
        delete[] facs[i];
      }

      mpz_clears(tmp, tmp2, NULL);
      return true;
    }

    bool isCarmichaelI(int n) {
      if (!isSquareFreeI(n)) return false;

      vector<int> facs;
      pollardRhoFactorI(n, facs);

      for (size_t i = 0; i < facs.size(); i++) {
        if ((n-1) % (facs[i]-1) != 0) return false;
      }

      return true;
    }
  }
}
<commit_msg>Fixed clean-up bug in isCarmichael.<commit_after>#include "Carmichael.h"
#include "SquareFree.h"
#include "PollardRho.h"

namespace Math {
  namespace Fac {
    bool isCarmichael(const mpz_t n) {
      if (!isSquareFree(n)) return false;

      vector<mpz_t*> facs;
      pollardRhoFactor(n, facs);

      mpz_t tmp, tmp2;
      mpz_inits(tmp, tmp2, NULL);
      mpz_sub_ui(tmp, n, 1);

      for (size_t i = 0; i < facs.size(); i++) {
        mpz_sub_ui(tmp2, *facs[i], 1);
        mpz_mod(tmp2, tmp, tmp2);
        if (mpz_cmp_ui(tmp2, 0) != 0) {
          for (size_t j = i; j < facs.size(); j++) {
            mpz_clear(*facs[j]);
            delete[] facs[j];
          }
          mpz_clears(tmp, tmp2, NULL);
          return false;
        }

        mpz_clear(*facs[i]);
        delete[] facs[i];
      }

      mpz_clears(tmp, tmp2, NULL);
      return true;
    }

    bool isCarmichaelI(int n) {
      if (!isSquareFreeI(n)) return false;

      vector<int> facs;
      pollardRhoFactorI(n, facs);

      for (size_t i = 0; i < facs.size(); i++) {
        if ((n-1) % (facs[i]-1) != 0) return false;
      }

      return true;
    }
  }
}
<|endoftext|>


<commit_before>#include "parser.h"
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace cirth {
namespace parser {

  namespace qi = boost::spirit::qi;
  namespace ascii = boost::spirit::ascii;
  namespace phx = boost::phoenix;
  using namespace cirth::ast;

  template <typename Iterator>
  struct grammar : qi::grammar<Iterator, std::vector<PTR<Expression> >(),
      ascii::space_type>{
    qi::rule<Iterator, std::vector<PTR<Expression> >(), ascii::space_type>
        program;
    qi::rule<Iterator, std::vector<PTR<Expression> >(), ascii::space_type>
        explist;
    qi::rule<Iterator, PTR<Expression>(), ascii::space_type> expression;
    qi::rule<Iterator, PTR<Expression>(), ascii::space_type> list;
    qi::rule<Iterator, std::vector<PTR<Expression> >(), ascii::space_type>
        appcommalist;
    qi::rule<Iterator, PTR<Expression>(), ascii::space_type> application;
    qi::rule<Iterator, PTR<Term>(), ascii::space_type> term;
    qi::rule<Iterator, std::vector<PTR<Term> >(), ascii::space_type> termlist;
    qi::rule<Iterator, PTR<Term>(), ascii::space_type> fullvalue;
    qi::rule<Iterator, PTR<Trailer>(), ascii::space_type> trailer;
    qi::rule<Iterator, PTR<Trailer>(), ascii::space_type> call;
    qi::rule<Iterator, PTR<Trailer>(), ascii::space_type> index;
    qi::rule<Iterator, PTR<Trailer>(), ascii::space_type> field;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> value;
    qi::rule<Iterator, PTR<Term>(), ascii::space_type> listexpansion;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> subexpression;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> function;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> valvariable;
    qi::rule<Iterator, Variable(), ascii::space_type> variable;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> integer;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> bytestring;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> charstring;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> floating;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> map;
    qi::rule<Iterator, MapDefinition(), ascii::space_type> mapdefinition;
    qi::rule<Iterator, std::vector<MapDefinition>(), ascii::space_type>
        mapdefinitionlist;
    qi::rule<Iterator, std::string(), ascii::space_type> identifier;
    qi::rule<Iterator, std::string(), ascii::space_type> charstringvalue;
    qi::rule<Iterator, std::string(), ascii::space_type> bytestringvalue;
    qi::rule<Iterator, ArgList(), ascii::space_type> arglist;
    qi::rule<Iterator, HalfArgs(), ascii::space_type> leftargs;
    qi::rule<Iterator, HalfArgs(), ascii::space_type> rightargs;
    qi::rule<Iterator, HalfArgs(), ascii::space_type> leftargsnoopts;
    qi::rule<Iterator, HalfArgs(), ascii::space_type> rightargsnoopts;
    qi::rule<Iterator, OptionalArgs(), ascii::space_type> leftoptargs;
    qi::rule<Iterator, OptionalArgs(), ascii::space_type> rightoptargs;
    qi::rule<Iterator, VarArg(), ascii::space_type> vararg;

    grammar() : grammar::base_type(program) {

      program = -explist;
      program.name("program");

      explist = expression % ';' >> -qi::lit(';');
      explist.name("expression list");

      appcommalist = application >> ',' >> -(application % ',');
      appcommalist.name("list");
      list = appcommalist[
          qi::_val = phx::construct<PTR<Expression> >(phx::new_<List>(qi::_1))];
      list.name("list");

      termlist = +term;
      termlist.name("application");
      application = termlist[qi::_val = phx::construct<PTR<Expression> >(
          phx::new_<Application>(qi::_1))];
      application.name("application");

      expression = list | application;
      expression.name("expression");

      term = fullvalue | listexpansion;
      term.name("term");

      listexpansion = (qi::lit("*(") > explist > ")")[
          qi::_val = phx::construct<PTR<Term> >(
          phx::new_<ListExpansion>(qi::_1))];
      listexpansion.name("list expansion");

      fullvalue = (value >> *trailer)[qi::_val = phx::construct<PTR<Term> >(
          phx::new_<FullValue>(qi::_1, qi::_2))];
      fullvalue.name("value with trailer");

      trailer = call | index | field;
      trailer.name("value trailer");

      call = qi::char_(".?")[qi::_val = phx::construct<PTR<Trailer> >(
          phx::new_<Call>())];
      call.name("call trailer");

      index = (qi::lit("[") > explist >> qi::lit("]"))[
          qi::_val = phx::construct<PTR<Trailer> >(phx::new_<Index>(qi::_1))];
      index.name("index trailer");

      field = (qi::lit(".") >> variable)[
          qi::_val = phx::construct<PTR<Trailer> >(phx::new_<Field>(qi::_1))];
      field.name("field trailer");

      value = subexpression
            | function
            | valvariable
            | integer
            | bytestring
            | charstring
            | floating
            | map;
      value.name("value");

      subexpression = (qi::lit("(") > explist > ")")[
          qi::_val = phx::construct<PTR<Value> >(
          phx::new_<SubExpression>(qi::_1))];
      subexpression.name("subexpression");

      identifier = qi::lexeme[qi::alpha >> *qi::alnum];
      identifier.name("identifier");

      variable = identifier[phx::bind(&Variable::name, qi::_val) = qi::_1];
      variable.name("variable");

      valvariable = identifier[qi::_val = phx::construct<PTR<Value> >(
          phx::new_<Variable>(qi::_1))];
      valvariable.name("variable value");

      integer = qi::long_long[qi::_val = phx::construct<PTR<Value> >(
          phx::new_<Integer>(qi::_1))];
      integer.name("integer");

      floating = qi::double_[qi::_val = phx::construct<PTR<Value> >(
          phx::new_<Float>(qi::_1))];
      floating.name("floating point number");

      charstringvalue = qi::lexeme['"' > +(qi::char_ - '"') > '"'];
      charstringvalue.name("character string");
      charstring = charstringvalue[qi::_val = phx::construct<PTR<Value> >(
          phx::new_<CharString>(qi::_1))];
      charstring.name("character string");

      bytestringvalue = qi::lexeme["b\"" > +(qi::char_ - '"') > '"'];
      bytestringvalue.name("byte string");
      bytestring = bytestringvalue[qi::_val = phx::construct<PTR<Value> >(
          phx::new_<ByteString>(qi::_1))];
      bytestring.name("byte string");

      map = (qi::lit("[") > mapdefinitionlist >> qi::lit("]"))[
          qi::_val = phx::construct<PTR<Value> >(phx::new_<Map>(qi::_1))];
      map.name("map");

      mapdefinitionlist = (*(mapdefinition >> qi::lit(',')) >> -(mapdefinition
          >> -qi::lit(',')))[qi::_val = qi::_1];
      mapdefinitionlist.name("map definition list");

      mapdefinition = (application >> ':' >> application)[phx::bind(
          &MapDefinition::key, qi::_val) = qi::_1, phx::bind(
          &MapDefinition::value, qi::_val) = qi::_2];
      mapdefinition.name("map definition");

      function = (qi::lit("{") > -arglist >> program >> qi::lit("}"))[
          qi::_val = phx::construct<PTR<Value> >(phx::new_<Function>(
          qi::_1, qi::_2))];
      function.name("function");
      arglist = (qi::lit("|") > -((leftargs | leftargsnoopts) >> ';') >>
          (rightargs | rightargsnoopts) >> qi::lit("|"))[
          phx::bind(&ArgList::leftargs, qi::_val) = qi::_1,
          phx::bind(&ArgList::rightargs, qi::_val) = qi::_2];
      arglist.name("argument list");
      vararg = (variable >> subexpression)[
          phx::bind(&VarArg::name, qi::_val) = qi::_1,
          phx::bind(&VarArg::subexpression, qi::_val) = qi::_2];
      vararg.name("variadic argument");
      leftoptargs = (-vararg >> *variable >> ',')[
          phx::bind(&OptionalArgs::var_arg, qi::_val) = qi::_1,
          phx::bind(&OptionalArgs::optional_args, qi::_val) = qi::_2];
      leftoptargs.name("left optional arguments");
      leftargs = (-leftoptargs >> *variable)[
          phx::bind(&HalfArgs::optional_args, qi::_val) = qi::_1,
          phx::bind(&HalfArgs::args, qi::_val) = qi::_2];
      leftargs.name("left arguments");
      leftargsnoopts = (vararg >> *variable)[
          phx::bind(&HalfArgs::var_arg, qi::_val) = qi::_1,
          phx::bind(&HalfArgs::args, qi::_val) = qi::_2];
      leftargsnoopts.name("left arguments with no optionals");
      rightoptargs = (qi::lit(',') >> *variable >> -vararg)[
          phx::bind(&OptionalArgs::optional_args, qi::_val) = qi::_1,
          phx::bind(&OptionalArgs::var_arg, qi::_val) = qi::_2];
      rightoptargs.name("right optional arguments");
      rightargs = (*variable >> -rightoptargs)[
          phx::bind(&HalfArgs::args, qi::_val) = qi::_1,
          phx::bind(&HalfArgs::optional_args, qi::_val) = qi::_2];
      rightargs.name("right arguments");
      rightargsnoopts = (*variable >> vararg)[
          phx::bind(&HalfArgs::var_arg, qi::_val) = qi::_2,
          phx::bind(&HalfArgs::args, qi::_val) = qi::_1];
      rightargsnoopts.name("right arguments with no optionals");

      qi::on_error<qi::fail>(explist,
        std::cout << phx::val("Error! Expecting ") << qi::_4
                  << phx::val(" here: \"")
                  << phx::construct<std::string>(qi::_3, qi::_2)
                  << phx::val("\"") << std::endl
      );

    }
  };
}}

bool cirth::parser::parse(const std::string& src,
    std::vector<PTR<cirth::ast::Expression> >& ast) {
  grammar<std::string::const_iterator> g;
  std::string::const_iterator iter = src.begin();
  bool r = boost::spirit::qi::phrase_parse(iter, src.end(), g,
      boost::spirit::ascii::space, ast);
  return r && iter == src.end();
}
<commit_msg>fixing some grammar things<commit_after>#include "parser.h"
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace cirth {
namespace parser {

  namespace qi = boost::spirit::qi;
  namespace ascii = boost::spirit::ascii;
  namespace phx = boost::phoenix;
  using namespace cirth::ast;

  template <typename Iterator>
  struct grammar : qi::grammar<Iterator, std::vector<PTR<Expression> >(),
      ascii::space_type>{
    qi::rule<Iterator, std::vector<PTR<Expression> >(), ascii::space_type>
        program;
    qi::rule<Iterator, std::vector<PTR<Expression> >(), ascii::space_type>
        explist;
    qi::rule<Iterator, PTR<Expression>(), ascii::space_type> expression;
    qi::rule<Iterator, PTR<Expression>(), ascii::space_type> list;
    qi::rule<Iterator, std::vector<PTR<Expression> >(), ascii::space_type>
        appcommalist;
    qi::rule<Iterator, PTR<Expression>(), ascii::space_type> application;
    qi::rule<Iterator, PTR<Term>(), ascii::space_type> term;
    qi::rule<Iterator, std::vector<PTR<Term> >(), ascii::space_type> termlist;
    qi::rule<Iterator, PTR<Term>(), ascii::space_type> fullvalue;
    qi::rule<Iterator, PTR<Trailer>(), ascii::space_type> trailer;
    qi::rule<Iterator, PTR<Trailer>(), ascii::space_type> call;
    qi::rule<Iterator, PTR<Trailer>(), ascii::space_type> index;
    qi::rule<Iterator, PTR<Trailer>(), ascii::space_type> field;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> value;
    qi::rule<Iterator, PTR<Term>(), ascii::space_type> listexpansion;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> subexpression;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> function;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> valvariable;
    qi::rule<Iterator, Variable(), ascii::space_type> variable;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> integer;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> bytestring;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> charstring;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> floating;
    qi::rule<Iterator, PTR<Value>(), ascii::space_type> map;
    qi::rule<Iterator, MapDefinition(), ascii::space_type> mapdefinition;
    qi::rule<Iterator, std::vector<MapDefinition>(), ascii::space_type>
        mapdefinitionlist;
    qi::rule<Iterator, std::string(), ascii::space_type> identifier;
    qi::rule<Iterator, std::string(), ascii::space_type> charstringvalue;
    qi::rule<Iterator, std::string(), ascii::space_type> bytestringvalue;
    qi::rule<Iterator, ArgList(), ascii::space_type> arglist;
    qi::rule<Iterator, HalfArgs(), ascii::space_type> leftargs;
    qi::rule<Iterator, HalfArgs(), ascii::space_type> rightargs;
    qi::rule<Iterator, HalfArgs(), ascii::space_type> leftargsnoopts;
    qi::rule<Iterator, HalfArgs(), ascii::space_type> rightargsnoopts;
    qi::rule<Iterator, OptionalArgs(), ascii::space_type> leftoptargs;
    qi::rule<Iterator, OptionalArgs(), ascii::space_type> rightoptargs;
    qi::rule<Iterator, VarArg(), ascii::space_type> vararg;

    grammar() : grammar::base_type(program) {

      program = -explist;
      program.name("program");

      explist = expression % ';' >> -qi::lit(';');
      explist.name("expression list");

      appcommalist = application >> ',' >> -(application % ',');
      appcommalist.name("list");
      list = appcommalist[
          qi::_val = phx::construct<PTR<Expression> >(phx::new_<List>(qi::_1))];
      list.name("list");

      termlist = +term;
      termlist.name("application");
      application = termlist[qi::_val = phx::construct<PTR<Expression> >(
          phx::new_<Application>(qi::_1))];
      application.name("application");

      expression = list | application;
      expression.name("expression");

      term = fullvalue | listexpansion;
      term.name("term");

      listexpansion = (qi::lit("*(") > explist >> ")")[
          qi::_val = phx::construct<PTR<Term> >(
          phx::new_<ListExpansion>(qi::_1))];
      listexpansion.name("list expansion");

      fullvalue = (value >> *trailer)[qi::_val = phx::construct<PTR<Term> >(
          phx::new_<FullValue>(qi::_1, qi::_2))];
      fullvalue.name("value with trailer");

      trailer = call | index | field;
      trailer.name("value trailer");

      call = qi::char_(".?")[qi::_val = phx::construct<PTR<Trailer> >(
          phx::new_<Call>())];
      call.name("call trailer");

      index = (qi::lit("[") > explist >> qi::lit("]"))[
          qi::_val = phx::construct<PTR<Trailer> >(phx::new_<Index>(qi::_1))];
      index.name("index trailer");

      field = (qi::lit(".") >> variable)[
          qi::_val = phx::construct<PTR<Trailer> >(phx::new_<Field>(qi::_1))];
      field.name("field trailer");

      value = subexpression
            | function
            | valvariable
            | integer
            | bytestring
            | charstring
            | floating
            | map;
      value.name("value");

      subexpression = (qi::lit("(") > explist >> ")")[
          qi::_val = phx::construct<PTR<Value> >(
          phx::new_<SubExpression>(qi::_1))];
      subexpression.name("subexpression");

      identifier = qi::lexeme[qi::alpha >> *qi::alnum];
      identifier.name("identifier");

      variable = identifier[phx::bind(&Variable::name, qi::_val) = qi::_1];
      variable.name("variable");

      valvariable = identifier[qi::_val = phx::construct<PTR<Value> >(
          phx::new_<Variable>(qi::_1))];
      valvariable.name("variable value");

      integer = qi::long_long[qi::_val = phx::construct<PTR<Value> >(
          phx::new_<Integer>(qi::_1))];
      integer.name("integer");

      floating = qi::double_[qi::_val = phx::construct<PTR<Value> >(
          phx::new_<Float>(qi::_1))];
      floating.name("floating point number");

      charstringvalue = qi::lexeme['"' > +(qi::char_ - '"') >> '"'];
      charstringvalue.name("character string");
      charstring = charstringvalue[qi::_val = phx::construct<PTR<Value> >(
          phx::new_<CharString>(qi::_1))];
      charstring.name("character string");

      bytestringvalue = qi::lexeme["b\"" > +(qi::char_ - '"') >> '"'];
      bytestringvalue.name("byte string");
      bytestring = bytestringvalue[qi::_val = phx::construct<PTR<Value> >(
          phx::new_<ByteString>(qi::_1))];
      bytestring.name("byte string");

      map = (qi::lit("[") > mapdefinitionlist >> qi::lit("]"))[
          qi::_val = phx::construct<PTR<Value> >(phx::new_<Map>(qi::_1))];
      map.name("map");

      mapdefinitionlist = (*(mapdefinition >> qi::lit(',')) >> -(mapdefinition
          >> -qi::lit(',')))[qi::_val = qi::_1];
      mapdefinitionlist.name("map definition list");

      mapdefinition = (application >> ':' >> application)[phx::bind(
          &MapDefinition::key, qi::_val) = qi::_1, phx::bind(
          &MapDefinition::value, qi::_val) = qi::_2];
      mapdefinition.name("map definition");

      function = (qi::lit("{") > -arglist >> program >> qi::lit("}"))[
          qi::_val = phx::construct<PTR<Value> >(phx::new_<Function>(
          qi::_1, qi::_2))];
      function.name("function");
      arglist = (qi::lit("|") > -((leftargs | leftargsnoopts) >> ';') >>
          (rightargs | rightargsnoopts) >> qi::lit("|"))[
          phx::bind(&ArgList::leftargs, qi::_val) = qi::_1,
          phx::bind(&ArgList::rightargs, qi::_val) = qi::_2];
      arglist.name("argument list");
      vararg = (variable >> subexpression)[
          phx::bind(&VarArg::name, qi::_val) = qi::_1,
          phx::bind(&VarArg::subexpression, qi::_val) = qi::_2];
      vararg.name("variadic argument");
      leftoptargs = (-vararg >> *variable >> ',')[
          phx::bind(&OptionalArgs::var_arg, qi::_val) = qi::_1,
          phx::bind(&OptionalArgs::optional_args, qi::_val) = qi::_2];
      leftoptargs.name("left optional arguments");
      leftargs = (-leftoptargs >> *variable)[
          phx::bind(&HalfArgs::optional_args, qi::_val) = qi::_1,
          phx::bind(&HalfArgs::args, qi::_val) = qi::_2];
      leftargs.name("left arguments");
      leftargsnoopts = (vararg >> *variable)[
          phx::bind(&HalfArgs::var_arg, qi::_val) = qi::_1,
          phx::bind(&HalfArgs::args, qi::_val) = qi::_2];
      leftargsnoopts.name("left arguments with no optionals");
      rightoptargs = (qi::lit(',') >> *variable >> -vararg)[
          phx::bind(&OptionalArgs::optional_args, qi::_val) = qi::_1,
          phx::bind(&OptionalArgs::var_arg, qi::_val) = qi::_2];
      rightoptargs.name("right optional arguments");
      rightargs = (*variable >> -rightoptargs)[
          phx::bind(&HalfArgs::args, qi::_val) = qi::_1,
          phx::bind(&HalfArgs::optional_args, qi::_val) = qi::_2];
      rightargs.name("right arguments");
      rightargsnoopts = (*variable >> vararg)[
          phx::bind(&HalfArgs::var_arg, qi::_val) = qi::_2,
          phx::bind(&HalfArgs::args, qi::_val) = qi::_1];
      rightargsnoopts.name("right arguments with no optionals");

      qi::on_error<qi::fail>(explist,
        std::cout << phx::val("Error! Expecting ") << qi::_4
                  << phx::val(" here: \"")
                  << phx::construct<std::string>(qi::_3, qi::_2)
                  << phx::val("\"") << std::endl
      );

    }
  };
}}

bool cirth::parser::parse(const std::string& src,
    std::vector<PTR<cirth::ast::Expression> >& ast) {
  grammar<std::string::const_iterator> g;
  std::string::const_iterator iter = src.begin();
  bool r = boost::spirit::qi::phrase_parse(iter, src.end(), g,
      boost::spirit::ascii::space, ast);
  return r && iter == src.end();
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2009 Roy Wellington IV
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <stack>
#include <cstdlib>
#include <climits>
#include <sstream>
#include <stdint.h>

#include "json.h"
#include "utf8_private.h"

/* Throw an exception if the passed stream is in an errored state.
 */
void check_stream(std::istream &s)
{
	if(!s)
	{
		if(s.eof())
			throw json::ParseException("Unexpected EOF in input stream.");
		else
			throw json::ParseException("I/O error in input.");
	}
}

/* Read a string from a stream.
 * If we can't read the passed string from the stream, throw an exception.
 */
void read_string(std::istream &s, const std::string &str)
{
	std::string::const_iterator i;
	char c;

	for(i = str.begin(); i != str.end(); ++i)
	{
		s.get(c);
		check_stream(s);
		if(c != *i)
			throw json::ParseException("Expected \"" + str + "\" input, but didn't find it.");
	}
}

/* Attempt to read a character from a stream.
 * Return true, and remove it from the stream if it is present.
 * Otherwise, return false.
 */
bool try_to_read(std::istream &s, char c_want)
{
	char c_have;

	c_have = s.peek();
	check_stream(s);

	if(c_have == c_want)
	{
		s.ignore();
		check_stream(s);
		return true;
	}
	return false;
}

/* Read digits from a stream, ie: [0-9]* or [0-9]+
 * If need_one is true, we must read at least one digit ([0-9]+)
 * Otherwise, no digits is acceptable ([0-9]*)
 * If need_one is true, and we can't read any digits, throw an exception.
 */
void read_digits_from_stream(std::istream &s, std::string &str, bool need_one)
{
	while(true)
	{
		char c = s.peek();
		check_stream(s);

		if(c >= '0' && c <= '9')
		{
			s.ignore();
			check_stream(s);
			str += c;
			need_one = false;
		}
		else
			break;
	}

	if(need_one)
	{
		// We needed a digit, but one didn't get read...
		throw json::ParseException();
	}
}

/* Read a numeric value, either integer or floating point, from the stream.
 * Return it as a json::Value -- this will be either a json::Double or a
 * json::Integer.
 */
json::Value *read_json_numeric(std::istream &s)
{
	std::string numeric_text;
	char c;

	if(try_to_read(s, '-'))
		numeric_text += '-';

	if(!try_to_read(s, '0'))
	{
		// Read in whole digits
		c = s.peek();
		check_stream(s);
		if(!(c >= '1' && c <= '9'))
		{
			throw json::ParseException();
		}

		numeric_text += c;
		s.ignore();
		check_stream(s);

		read_digits_from_stream(s, numeric_text, false);
	}
	else
	{
		// It was a 0.
		numeric_text += '0';
	}

	if(try_to_read(s, '.'))
	{
		// A decimal point.
		numeric_text += '.';

		// Read the digits!
		read_digits_from_stream(s, numeric_text, true);
	}

	c = s.peek();
	check_stream(s);
	if(c == 'e' || c == 'E')
	{
		s.ignore();
		check_stream(s);
		numeric_text += 'e';

		read_digits_from_stream(s, numeric_text, true);
	}

	intmax_t ll;
	char *endptr;

	ll = strtoll(numeric_text.c_str(), &endptr, 10);
	if(endptr == numeric_text.c_str() + numeric_text.size())
	{
		// Yay, a number.
		if(ll <= INT_MAX && ll >= INT_MIN)
		{
			return new json::Integer(ll);
		}
		else return new json::BigInteger(ll);
	}

	double d;

	d = strtod(numeric_text.c_str(), &endptr);
	if(endptr == numeric_text.c_str() + numeric_text.size())
	{
		return new json::Double(d);
	}

	throw json::ParseException();
}

void read_four_hex_digits(std::istream &s, char *four_hex)
{
	for(size_t i = 0; i < 4; ++i)
	{
		four_hex[i] = s.get();
		check_stream(s);
		if(!((four_hex[i] >= '0' && four_hex[i] <= '9')
			|| (four_hex[i] >= 'A' && four_hex[i] <= 'F')
			|| (four_hex[i] >= 'a' && four_hex[i] <= 'f')))
		{
			throw json::ParseException();
		}
	}
}

/* Read a JSON string from a stream. This returns the actual string, and not
 * a json::String as it gets used to read keys in objects as well.
 * A JSON string is:
 *   "text... "
 * ie, an opening quote, characters, a closing quote. There are some escape
 * sequences for things like quotes or slashes.
 */
std::string read_json_string_basic(std::istream &s)
{
	char c;
	std::string text;

	// Read opening quote.
	c = s.get();
	check_stream(s);
	if(!s || c != '\"')
		throw json::ParseException();

	while(true)
	{
		c = s.get();
		check_stream(s);

		if(c == '\"')
			break;

		// Characters below 0x20 must be escaped.
		if(c < 0x20)
			throw json::ParseException();

		if(c == '\\')
		{
			char escape_code = s.get();
			check_stream(s);

			if(escape_code == '\"')
				text += '\"';
			else if(escape_code == '\\')
				text += '\\';
			else if(escape_code == '/')
				text += '/';
			else if(escape_code == 'b')
				text += 0x08;
			else if(escape_code == 'f')
				text += 0x0C;
			else if(escape_code == 'n')
				text += '\n';
			else if(escape_code == 'r')
				text += '\r';
			else if(escape_code == 't')
				text += '\t';
			else if(escape_code == 'u')
			{
				char four_hex[5] = {0};
				read_four_hex_digits(s, four_hex);

				unsigned int code = strtoul(four_hex, NULL, 16);
				int surrogate_type = is_surrogate_pair(code);
				if(surrogate_type == 0)
					text += to_utf8(code);
				else if(surrogate_type == 1)
				{
					// The last \u escape was the start marker of a surrogate pair.
					// Look for the second half of the pair.
					read_string(s, "\\u");
					char four_hex_2[5] = {0};
					read_four_hex_digits(s, four_hex_2);
					unsigned int code2 = strtoul(four_hex_2, NULL, 16);
					if(is_surrogate_pair(code2) != 2)
						// There was another \u escape, but it wasn't the second half
						// of the surrogate pair...
						throw json::ParseException();
					uint32_t code_point = decode_surrogate_pair(code, code2);
					text += to_utf8(code);
				}
				else
					// We found the second half of a surrogate pair, but there wasn't
					// a first half...
					throw json::ParseException();
			}
			else
				throw json::ParseException();
		}
		else
			text += c;
	}

	check_is_valid_utf8(text);
	return text;
}

/* Remove as much whitespace (tabs, newlines, carriage returns or spaces)
 * from the front of a string as possible.
 */
void eat_whitespace(std::istream &s)
{
	char c;

	while(true)
	{
		c = s.peek();
		check_stream(s);

		if(c == ' ' || c == '\t' || c == '\r' || c == '\n')
		{
			s.ignore();
			check_stream(s);
		}
		else
			break;
	}
}

/* Read in a JSON object or array start marker from the stream,
 * and create and return a JSON object/array to store it.
 */
json::Value *read_json_object_or_array(std::istream &s)
{
	char c;

	c = s.peek();
	check_stream(s);

	if(c == '[')
	{
		s.ignore();
		check_stream(s);
		eat_whitespace(s);
		return new json::Array();
	}
	else if(c == '{')
	{
		s.ignore();
		check_stream(s);
		eat_whitespace(s);
		return new json::Object();
	}
	else
		throw json::ParseException();
}

/* Read in a JSON value from the stream. We should be positioned right before
 * it.
 */
json::Value *read_json_value(std::istream &s)
{
	// Peek a character - this tells us what type we're looking at.
	// All the JSON primitives differ in their first character.
	char c;
	c = s.peek();
	check_stream(s);

	if(c == 'n')
	{
		read_string(s, "null");
		return new json::Null();
	}
	else if(c == 't')
	{
		read_string(s, "true");
		return new json::Bool(true);
	}
	else if(c == 'f')
	{
		read_string(s, "false");
		return new json::Bool(false);
	}
	else if(c == '\"')
	{
		std::string str;
		str = read_json_string_basic(s);
		return new json::String(str);
	}
	else if(c == '-'
		|| (c >= '0' && c <= '9'))
	{
		return read_json_numeric(s);
	}
	// By now, it should be an array or an object -- this function will throw
	// if it isn't.
	else return read_json_object_or_array(s);
}

void parse_item(std::istream &s, std::stack<json::Value *> &struct_stack)
{
	// Get the object/array:
	json::Array *arr = NULL;
	json::Object *obj = NULL;
	if(struct_stack.top()->type() == json::TYPE_ARRAY)
		arr = dynamic_cast<json::Array *>(struct_stack.top());
	else
		obj = dynamic_cast<json::Object *>(struct_stack.top());

	// See if we've reached the end:
	char c = s.peek();
	check_stream(s);
	if((arr && c == ']')
		|| (obj && c == '}'))
	{
		s.ignore();
		check_stream(s);
		struct_stack.pop();

		if(!struct_stack.empty())
		{
			eat_whitespace(s);
		}

		return;
	}

	// Check for a comma:
	if((arr && !arr->empty())
		|| (obj && !obj->empty()))
	{
		if(c != ',')
			throw json::ParseException("Expected \',\' token.");

		s.ignore();
		check_stream(s);
		eat_whitespace(s);
	}

	// Read in a key if this is an object
	std::string key;
	if(obj)
	{
		key = read_json_string_basic(s);
		eat_whitespace(s);
		char colon = s.get();
		check_stream(s);
		if(colon != ':')
			throw json::ParseException("Expected \':\' token.");
		eat_whitespace(s);
	}

	// Read in the actual value:
	json::Value *v = NULL;
	try
	{
		v = read_json_value(s);
		if(arr)
			arr->pushBackTake(v);
		else
			obj->takeValue(key, v);
	}
	catch(...)
	{
		delete v;
		throw;
	}

	if(v->type() == json::TYPE_ARRAY
		|| v->type() == json::TYPE_OBJECT)
	{
		struct_stack.push(v);
	}

	eat_whitespace(s);
}

json::Value *json::parse(std::istream &s)
{
	std::stack<json::Value *> struct_stack;
	json::Value *base_object = NULL;

	eat_whitespace(s);
	try
	{
		struct_stack.push(base_object = read_json_object_or_array(s));

		while(!struct_stack.empty())
		{
			parse_item(s, struct_stack);
		}
	}
	catch(...)
	{
		delete base_object;
		throw;
	}
	return base_object;
}

json::Value *json::parse(const std::string &s)
{
	std::stringstream ss;

	ss.str(s);
	return json::parse(ss);
}
<commit_msg>Fixed minor bug with characters >0x7f.<commit_after>/*
 * Copyright (c) 2009 Roy Wellington IV
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <stack>
#include <cstdlib>
#include <climits>
#include <sstream>
#include <stdint.h>

#include "json.h"
#include "utf8_private.h"

/* Throw an exception if the passed stream is in an errored state.
 */
void check_stream(std::istream &s)
{
	if(!s)
	{
		if(s.eof())
			throw json::ParseException("Unexpected EOF in input stream.");
		else
			throw json::ParseException("I/O error in input.");
	}
}

/* Read a character from a stream, and then check the stream for errors.
 */
char checked_stream_get(std::istream &s)
{
	char c;
	s.get(c);
	check_stream(s);
	return c;
}

/* Read a string from a stream.
 * If we can't read the passed string from the stream, throw an exception.
 */
void read_string(std::istream &s, const std::string &str)
{
	std::string::const_iterator i;
	char c;

	for(i = str.begin(); i != str.end(); ++i)
	{
		c = checked_stream_get(s);
		if(c != *i)
			throw json::ParseException("Expected \"" + str + "\" input, but didn't find it.");
	}
}

/* Attempt to read a character from a stream.
 * Return true, and remove it from the stream if it is present.
 * Otherwise, return false.
 */
bool try_to_read(std::istream &s, char c_want)
{
	char c_have;

	c_have = s.peek();
	check_stream(s);

	if(c_have == c_want)
	{
		s.ignore();
		check_stream(s);
		return true;
	}
	return false;
}

/* Read digits from a stream, ie: [0-9]* or [0-9]+
 * If need_one is true, we must read at least one digit ([0-9]+)
 * Otherwise, no digits is acceptable ([0-9]*)
 * If need_one is true, and we can't read any digits, throw an exception.
 */
void read_digits_from_stream(std::istream &s, std::string &str, bool need_one)
{
	while(true)
	{
		char c = s.peek();
		check_stream(s);

		if(c >= '0' && c <= '9')
		{
			s.ignore();
			check_stream(s);
			str += c;
			need_one = false;
		}
		else
			break;
	}

	if(need_one)
	{
		// We needed a digit, but one didn't get read...
		throw json::ParseException();
	}
}

/* Read a numeric value, either integer or floating point, from the stream.
 * Return it as a json::Value -- this will be either a json::Double or a
 * json::Integer.
 */
json::Value *read_json_numeric(std::istream &s)
{
	std::string numeric_text;
	char c;

	if(try_to_read(s, '-'))
		numeric_text += '-';

	if(!try_to_read(s, '0'))
	{
		// Read in whole digits
		c = s.peek();
		check_stream(s);
		if(!(c >= '1' && c <= '9'))
		{
			throw json::ParseException();
		}

		numeric_text += c;
		s.ignore();
		check_stream(s);

		read_digits_from_stream(s, numeric_text, false);
	}
	else
	{
		// It was a 0.
		numeric_text += '0';
	}

	if(try_to_read(s, '.'))
	{
		// A decimal point.
		numeric_text += '.';

		// Read the digits!
		read_digits_from_stream(s, numeric_text, true);
	}

	c = s.peek();
	check_stream(s);
	if(c == 'e' || c == 'E')
	{
		s.ignore();
		check_stream(s);
		numeric_text += 'e';

		read_digits_from_stream(s, numeric_text, true);
	}

	intmax_t ll;
	char *endptr;

	ll = strtoll(numeric_text.c_str(), &endptr, 10);
	if(endptr == numeric_text.c_str() + numeric_text.size())
	{
		// Yay, a number.
		if(ll <= INT_MAX && ll >= INT_MIN)
		{
			return new json::Integer(ll);
		}
		else return new json::BigInteger(ll);
	}

	double d;

	d = strtod(numeric_text.c_str(), &endptr);
	if(endptr == numeric_text.c_str() + numeric_text.size())
	{
		return new json::Double(d);
	}

	throw json::ParseException();
}

void read_four_hex_digits(std::istream &s, char *four_hex)
{
	for(size_t i = 0; i < 4; ++i)
	{
		four_hex[i] = checked_stream_get(s);
		if(!((four_hex[i] >= '0' && four_hex[i] <= '9')
			|| (four_hex[i] >= 'A' && four_hex[i] <= 'F')
			|| (four_hex[i] >= 'a' && four_hex[i] <= 'f')))
		{
			throw json::ParseException();
		}
	}
}

/* Read a JSON string from a stream. This returns the actual string, and not
 * a json::String as it gets used to read keys in objects as well.
 * A JSON string is:
 *   "text... "
 * ie, an opening quote, characters, a closing quote. There are some escape
 * sequences for things like quotes or slashes.
 */
std::string read_json_string_basic(std::istream &s)
{
	char c;
	std::string text;

	// Read opening quote.
	c = s.get();
	check_stream(s);
	if(!s || c != '\"')
		throw json::ParseException();

	while(true)
	{
		c = s.get();
		check_stream(s);

		if(c == '\"')
			break;

		// Characters below 0x20 must be escaped.
		if((unsigned char)c < 0x20)
			throw json::ParseException();

		if(c == '\\')
		{
			char escape_code = s.get();
			check_stream(s);

			if(escape_code == '\"')
				text += '\"';
			else if(escape_code == '\\')
				text += '\\';
			else if(escape_code == '/')
				text += '/';
			else if(escape_code == 'b')
				text += 0x08;
			else if(escape_code == 'f')
				text += 0x0C;
			else if(escape_code == 'n')
				text += '\n';
			else if(escape_code == 'r')
				text += '\r';
			else if(escape_code == 't')
				text += '\t';
			else if(escape_code == 'u')
			{
				char four_hex[5] = {0};
				read_four_hex_digits(s, four_hex);

				unsigned int code = strtoul(four_hex, NULL, 16);
				int surrogate_type = is_surrogate_pair(code);
				if(surrogate_type == 0)
					text += to_utf8(code);
				else if(surrogate_type == 1)
				{
					// The last \u escape was the start marker of a surrogate pair.
					// Look for the second half of the pair.
					read_string(s, "\\u");
					char four_hex_2[5] = {0};
					read_four_hex_digits(s, four_hex_2);
					unsigned int code2 = strtoul(four_hex_2, NULL, 16);
					if(is_surrogate_pair(code2) != 2)
						// There was another \u escape, but it wasn't the second half
						// of the surrogate pair...
						throw json::ParseException();
					uint32_t code_point = decode_surrogate_pair(code, code2);
					text += to_utf8(code);
				}
				else
					// We found the second half of a surrogate pair, but there wasn't
					// a first half...
					throw json::ParseException();
			}
			else
				throw json::ParseException();
		}
		else
			text += c;
	}

	check_is_valid_utf8(text);
	return text;
}

/* Remove as much whitespace (tabs, newlines, carriage returns or spaces)
 * from the front of a string as possible.
 */
void eat_whitespace(std::istream &s)
{
	char c;

	while(true)
	{
		c = s.peek();
		check_stream(s);

		if(c == ' ' || c == '\t' || c == '\r' || c == '\n')
		{
			s.ignore();
			check_stream(s);
		}
		else
			break;
	}
}

/* Read in a JSON object or array start marker from the stream,
 * and create and return a JSON object/array to store it.
 */
json::Value *read_json_object_or_array(std::istream &s)
{
	char c;

	c = s.peek();
	check_stream(s);

	if(c == '[')
	{
		s.ignore();
		check_stream(s);
		eat_whitespace(s);
		return new json::Array();
	}
	else if(c == '{')
	{
		s.ignore();
		check_stream(s);
		eat_whitespace(s);
		return new json::Object();
	}
	else
		throw json::ParseException();
}

/* Read in a JSON value from the stream. We should be positioned right before
 * it.
 */
json::Value *read_json_value(std::istream &s)
{
	// Peek a character - this tells us what type we're looking at.
	// All the JSON primitives differ in their first character.
	char c;
	c = s.peek();
	check_stream(s);

	if(c == 'n')
	{
		read_string(s, "null");
		return new json::Null();
	}
	else if(c == 't')
	{
		read_string(s, "true");
		return new json::Bool(true);
	}
	else if(c == 'f')
	{
		read_string(s, "false");
		return new json::Bool(false);
	}
	else if(c == '\"')
	{
		std::string str;
		str = read_json_string_basic(s);
		return new json::String(str);
	}
	else if(c == '-'
		|| (c >= '0' && c <= '9'))
	{
		return read_json_numeric(s);
	}
	// By now, it should be an array or an object -- this function will throw
	// if it isn't.
	else return read_json_object_or_array(s);
}

void parse_item(std::istream &s, std::stack<json::Value *> &struct_stack)
{
	// Get the object/array:
	json::Array *arr = NULL;
	json::Object *obj = NULL;
	if(struct_stack.top()->type() == json::TYPE_ARRAY)
		arr = dynamic_cast<json::Array *>(struct_stack.top());
	else
		obj = dynamic_cast<json::Object *>(struct_stack.top());

	// See if we've reached the end:
	char c = s.peek();
	check_stream(s);
	if((arr && c == ']')
		|| (obj && c == '}'))
	{
		s.ignore();
		check_stream(s);
		struct_stack.pop();

		if(!struct_stack.empty())
		{
			eat_whitespace(s);
		}

		return;
	}

	// Check for a comma:
	if((arr && !arr->empty())
		|| (obj && !obj->empty()))
	{
		if(c != ',')
			throw json::ParseException("Expected \',\' token.");

		s.ignore();
		check_stream(s);
		eat_whitespace(s);
	}

	// Read in a key if this is an object
	std::string key;
	if(obj)
	{
		key = read_json_string_basic(s);
		eat_whitespace(s);
		if(checked_stream_get(s) != ':')
			throw json::ParseException("Expected \':\' token.");
		eat_whitespace(s);
	}

	// Read in the actual value:
	json::Value *v = NULL;
	try
	{
		v = read_json_value(s);
		if(arr)
			arr->pushBackTake(v);
		else
			obj->takeValue(key, v);
	}
	catch(...)
	{
		delete v;
		throw;
	}

	if(v->type() == json::TYPE_ARRAY
		|| v->type() == json::TYPE_OBJECT)
	{
		struct_stack.push(v);
	}

	eat_whitespace(s);
}

json::Value *json::parse(std::istream &s)
{
	std::stack<json::Value *> struct_stack;
	json::Value *base_object = NULL;

	eat_whitespace(s);
	try
	{
		struct_stack.push(base_object = read_json_object_or_array(s));

		while(!struct_stack.empty())
		{
			parse_item(s, struct_stack);
		}
	}
	catch(...)
	{
		delete base_object;
		throw;
	}
	return base_object;
}

json::Value *json::parse(const std::string &s)
{
	std::stringstream ss;

	ss.str(s);
	return json::parse(ss);
}
<|endoftext|>


<commit_before>/*
 * MRustC - Rust Compiler
 * - By John Hodge (Mutabah/thePowersGang)
 *
 * trans/auto_impls.cpp
 * - Automatic trait/method impls
 *
 * Handles implementing Clone (when in 1.29 mode)
 */
#include "main_bindings.hpp"
#include "trans_list.hpp"
#include <hir/hir.hpp>
#include <mir/mir.hpp>
#include <hir_typeck/common.hpp>    // monomorph
#include <hir_typeck/static.hpp>    // StaticTraitResolve
#include <deque>
#include <algorithm>    // find_if

namespace {
    struct State
    {
        ::HIR::Crate&   crate;
        StaticTraitResolve resolve;
        const TransList& trans_list;
        ::std::deque<::HIR::TypeRef>    todo_list;
        ::std::set<::HIR::TypeRef>  done_list;

        ::HIR::SimplePath  lang_Clone;

        State(::HIR::Crate& crate, const TransList& trans_list):
            crate(crate),
            resolve( crate ),
            trans_list(trans_list)
        {
            lang_Clone = crate.get_lang_item_path(Span(), "clone");
        }

        void enqueue_type(const ::HIR::TypeRef& ty) {
            if( this->trans_list.auto_clone_impls.count(ty) == 0 && this->done_list.count(ty) == 0 ) {
                this->done_list.insert( ty.clone() );
                this->todo_list.push_back( ty.clone() );
            }
        }
    };
}

namespace {
    ::MIR::Param clone_field(const State& state, const Span& sp, ::MIR::Function& mir_fcn, const ::HIR::TypeRef& subty, ::MIR::LValue fld_lvalue)
    {
        if( state.resolve.type_is_copy(sp, subty) )
        {
            return ::std::move(fld_lvalue);
        }
        else
        {
            const auto& lang_Clone = state.resolve.m_crate.get_lang_item_path(sp, "clone");
            // Allocate to locals (one for the `&T`, the other for the cloned `T`)
            auto borrow_lv = ::MIR::LValue::new_Local( mir_fcn.locals.size() );
            mir_fcn.locals.push_back(::HIR::TypeRef::new_borrow(::HIR::BorrowType::Shared, subty.clone()));
            auto res_lv = ::MIR::LValue::new_Local( mir_fcn.locals.size() );
            mir_fcn.locals.push_back(subty.clone());

            // Call `<T as Clone>::clone`, passing a borrow of the field
            ::MIR::BasicBlock   bb;
            bb.statements.push_back(::MIR::Statement::make_Assign({
                    borrow_lv.clone(),
                    ::MIR::RValue::make_Borrow({ 0, ::HIR::BorrowType::Shared, mv$(fld_lvalue) })
                    }));
            bb.terminator = ::MIR::Terminator::make_Call({
                    static_cast<unsigned>(mir_fcn.blocks.size() + 2),  // return block (after the panic block below)
                    static_cast<unsigned>(mir_fcn.blocks.size() + 1),  // panic block (next block)
                    res_lv.clone(),
                    ::MIR::CallTarget( ::HIR::Path(subty.clone(), lang_Clone, "clone") ),
                    ::make_vec1<::MIR::Param>( ::std::move(borrow_lv) )
                    });
            mir_fcn.blocks.push_back(::std::move( bb ));

            // Stub panic handling (TODO: Make this iterate `values` and drop all of them)
            ::MIR::BasicBlock   panic_bb;
            bb.terminator = ::MIR::Terminator::make_Diverge({});
            mir_fcn.blocks.push_back(::std::move( panic_bb ));

            // Save the output of the `clone` call
            return ::std::move(res_lv);
        }
    }
}

void Trans_AutoImpl_Clone(State& state, ::HIR::TypeRef ty)
{
    Span    sp;
    TRACE_FUNCTION_F(ty);

    // Create MIR
    ::MIR::Function mir_fcn;
    if( state.resolve.type_is_copy(sp, ty) )
    {
        ::MIR::BasicBlock   bb;
        bb.statements.push_back(::MIR::Statement::make_Assign({
            ::MIR::LValue::new_Return(),
            ::MIR::RValue::make_Use( ::MIR::LValue::new_Deref( ::MIR::LValue::new_Argument(0) ) )
            }));
        bb.terminator = ::MIR::Terminator::make_Return({});
        mir_fcn.blocks.push_back(::std::move( bb ));
    }
    else
    {
        TU_MATCH_HDRA( (ty.m_data), {)
        default:
            TODO(sp, "auto Clone for " << ty << " - Unknown and not Copy");
        TU_ARMA(Path, te) {
            // closures are identified by the name starting with 'closure#'
            if( TU_TEST1(te.path.m_data, Generic, .m_path.m_components.back().compare(0, 8, "closure#") == 0) ) {
                const auto& gp = te.path.m_data.as_Generic();
                const auto& str = state.resolve.m_crate.get_struct_by_path(sp, gp.m_path);
                Trans_Params p;
                p.sp = sp;
                p.pp_impl = gp.m_params.clone();
                ::std::vector< ::MIR::Param>   values; values.reserve( str.m_data.as_Tuple().size() );
                for(const auto& fld : str.m_data.as_Tuple())
                {
                    ::HIR::TypeRef  tmp;
                    const auto& ty_m = monomorphise_type_needed(fld.ent) ? (tmp = p.monomorph(state.resolve, fld.ent)) : fld.ent;
                    auto fld_lvalue = ::MIR::LValue::new_Field( ::MIR::LValue::new_Deref(::MIR::LValue::new_Argument(0)), static_cast<unsigned>(values.size()) );
                    values.push_back( clone_field(state, sp, mir_fcn, ty_m, mv$(fld_lvalue)) );
                }
                // Construct the result value
                ::MIR::BasicBlock   bb;
                bb.statements.push_back(::MIR::Statement::make_Assign({
                    ::MIR::LValue::new_Return(),
                    ::MIR::RValue::make_Struct({ gp.clone(), mv$(values) })
                    }));
                bb.terminator = ::MIR::Terminator::make_Return({});
                mir_fcn.blocks.push_back(::std::move( bb ));
            }
            else {
                TODO(sp, "auto Clone for " << ty << " - Unknown and not Copy");
            }
            }
        TU_ARMA(Array, te) {
            ASSERT_BUG(sp, te.size_val < 256, "TODO: Is more than 256 elements sane for auto-generated non-Copy Clone impl? " << ty);
            ::std::vector< ::MIR::Param>   values; values.reserve(te.size_val);
            for(size_t i = 0; i < te.size_val; i ++)
            {
                auto fld_lvalue = ::MIR::LValue::new_Field( ::MIR::LValue::new_Deref(::MIR::LValue::new_Argument(0)), static_cast<unsigned>(values.size()) );
                values.push_back( clone_field(state, sp, mir_fcn, *te.inner, mv$(fld_lvalue)) );
            }
            // Construct the result
            ::MIR::BasicBlock   bb;
            bb.statements.push_back(::MIR::Statement::make_Assign({
                ::MIR::LValue::new_Return(),
                ::MIR::RValue::make_Array({ mv$(values) })
                }));
            bb.terminator = ::MIR::Terminator::make_Return({});
            mir_fcn.blocks.push_back(::std::move( bb ));
            }
        TU_ARMA(Tuple, te) {
            assert(te.size() > 0);

            ::std::vector< ::MIR::Param>   values; values.reserve(te.size());
            // For each field of the tuple, create a clone (either using Copy if posible, or calling Clone::clone)
            for(const auto& subty : te)
            {
                auto fld_lvalue = ::MIR::LValue::new_Field( ::MIR::LValue::new_Deref(::MIR::LValue::new_Argument(0)), static_cast<unsigned>(values.size()) );
                values.push_back( clone_field(state, sp, mir_fcn, subty, mv$(fld_lvalue)) );
            }

            // Construct the result tuple
            ::MIR::BasicBlock   bb;
            bb.statements.push_back(::MIR::Statement::make_Assign({
                ::MIR::LValue::new_Return(),
                ::MIR::RValue::make_Tuple({ mv$(values) })
                }));
            bb.terminator = ::MIR::Terminator::make_Return({});
            mir_fcn.blocks.push_back(::std::move( bb ));
            }
        }
    }

    // Function
    ::HIR::Function fcn {
        /*m_save_code=*/false,
        ::HIR::Linkage {},
        ::HIR::Function::Receiver::BorrowShared,
        /*m_abi=*/ABI_RUST,
        /*m_unsafe =*/false,
        /*m_const=*/false,
        ::HIR::GenericParams {},
        /*m_args=*/::make_vec1(::std::make_pair(
            ::HIR::Pattern( ::HIR::PatternBinding(false, ::HIR::PatternBinding::Type::Move, "self", 0), ::HIR::Pattern::Data::make_Any({}) ),
            ::HIR::TypeRef::new_borrow(::HIR::BorrowType::Shared, ty.clone())
            )),
        /*m_variadic=*/false,
        /*m_return=*/ty.clone(),
        ::HIR::ExprPtr {}
        };
    fcn.m_code.m_mir = ::MIR::FunctionPointer( new ::MIR::Function(mv$(mir_fcn)) );

    // Impl
    ::HIR::TraitImpl    impl;
    impl.m_type = mv$(ty);
    impl.m_methods.insert(::std::make_pair( RcString::new_interned("clone"), ::HIR::TraitImpl::ImplEnt< ::HIR::Function> { false, ::std::move(fcn) } ));

    // Add impl to the crate
    auto& list = state.crate.m_trait_impls[state.lang_Clone].get_list_for_type_mut(impl.m_type);
    list.push_back( box$(impl) );
}

void Trans_AutoImpls(::HIR::Crate& crate, TransList& trans_list)
{
    if( TARGETVER_1_19 )
        return ;

    State   state { crate, trans_list };

    // Generate for all
    for(const auto& ty : trans_list.auto_clone_impls)
    {
        state.done_list.insert( ty.clone() );
        Trans_AutoImpl_Clone(state, ty.clone());
    }

    while( !state.todo_list.empty() )
    {
        auto ty = ::std::move(state.todo_list.front());
        state.todo_list.pop_back();

        Trans_AutoImpl_Clone(state, mv$(ty));
    }

    auto impl_list_it = crate.m_trait_impls.find(state.lang_Clone);
    for(const auto& ty : state.done_list)
    {
        assert(impl_list_it != crate.m_trait_impls.end());
        // TODO: Find a way of turning a set into a vector so items can be erased.

        auto p = ::HIR::Path(ty.clone(), ::HIR::GenericPath(state.lang_Clone), "clone");
        //DEBUG("add_function(" << p << ")");
        auto e = trans_list.add_function(::std::move(p));

        const auto* impl_list = impl_list_it->second.get_list_for_type(ty);
        ASSERT_BUG(Span(), impl_list, "No impl list of Clone for " << ty);
        auto& impl = **::std::find_if( impl_list->begin(), impl_list->end(), [&](const auto& i){ return i->m_type == ty; });
        assert( impl.m_methods.size() == 1 );
        e->ptr = &impl.m_methods.begin()->second.data;
    }
}

<commit_msg>Trans AutoImpls - Fix minor issue in generation of Clone panic arms<commit_after>/*
 * MRustC - Rust Compiler
 * - By John Hodge (Mutabah/thePowersGang)
 *
 * trans/auto_impls.cpp
 * - Automatic trait/method impls
 *
 * Handles implementing Clone (when in 1.29 mode)
 */
#include "main_bindings.hpp"
#include "trans_list.hpp"
#include <hir/hir.hpp>
#include <mir/mir.hpp>
#include <hir_typeck/common.hpp>    // monomorph
#include <hir_typeck/static.hpp>    // StaticTraitResolve
#include <deque>
#include <algorithm>    // find_if

namespace {
    struct State
    {
        ::HIR::Crate&   crate;
        StaticTraitResolve resolve;
        const TransList& trans_list;
        ::std::deque<::HIR::TypeRef>    todo_list;
        ::std::set<::HIR::TypeRef>  done_list;

        ::HIR::SimplePath  lang_Clone;

        State(::HIR::Crate& crate, const TransList& trans_list):
            crate(crate),
            resolve( crate ),
            trans_list(trans_list)
        {
            lang_Clone = crate.get_lang_item_path(Span(), "clone");
        }

        void enqueue_type(const ::HIR::TypeRef& ty) {
            if( this->trans_list.auto_clone_impls.count(ty) == 0 && this->done_list.count(ty) == 0 ) {
                this->done_list.insert( ty.clone() );
                this->todo_list.push_back( ty.clone() );
            }
        }
    };
}

namespace {
    ::MIR::Param clone_field(const State& state, const Span& sp, ::MIR::Function& mir_fcn, const ::HIR::TypeRef& subty, ::MIR::LValue fld_lvalue)
    {
        if( state.resolve.type_is_copy(sp, subty) )
        {
            return ::std::move(fld_lvalue);
        }
        else
        {
            const auto& lang_Clone = state.resolve.m_crate.get_lang_item_path(sp, "clone");
            // Allocate to locals (one for the `&T`, the other for the cloned `T`)
            auto borrow_lv = ::MIR::LValue::new_Local( mir_fcn.locals.size() );
            mir_fcn.locals.push_back(::HIR::TypeRef::new_borrow(::HIR::BorrowType::Shared, subty.clone()));
            auto res_lv = ::MIR::LValue::new_Local( mir_fcn.locals.size() );
            mir_fcn.locals.push_back(subty.clone());

            // Call `<T as Clone>::clone`, passing a borrow of the field
            ::MIR::BasicBlock   bb;
            bb.statements.push_back(::MIR::Statement::make_Assign({
                    borrow_lv.clone(),
                    ::MIR::RValue::make_Borrow({ 0, ::HIR::BorrowType::Shared, mv$(fld_lvalue) })
                    }));
            bb.terminator = ::MIR::Terminator::make_Call({
                    static_cast<unsigned>(mir_fcn.blocks.size() + 2),  // return block (after the panic block below)
                    static_cast<unsigned>(mir_fcn.blocks.size() + 1),  // panic block (next block)
                    res_lv.clone(),
                    ::MIR::CallTarget( ::HIR::Path(subty.clone(), lang_Clone, "clone") ),
                    ::make_vec1<::MIR::Param>( ::std::move(borrow_lv) )
                    });
            mir_fcn.blocks.push_back(::std::move( bb ));

            // Stub panic handling (TODO: Make this iterate `values` and drop all of them)
            ::MIR::BasicBlock   panic_bb;
            panic_bb.terminator = ::MIR::Terminator::make_Diverge({});
            mir_fcn.blocks.push_back(::std::move( panic_bb ));

            // Save the output of the `clone` call
            return ::std::move(res_lv);
        }
    }
}

void Trans_AutoImpl_Clone(State& state, ::HIR::TypeRef ty)
{
    Span    sp;
    TRACE_FUNCTION_F(ty);

    // Create MIR
    ::MIR::Function mir_fcn;
    if( state.resolve.type_is_copy(sp, ty) )
    {
        ::MIR::BasicBlock   bb;
        bb.statements.push_back(::MIR::Statement::make_Assign({
            ::MIR::LValue::new_Return(),
            ::MIR::RValue::make_Use( ::MIR::LValue::new_Deref( ::MIR::LValue::new_Argument(0) ) )
            }));
        bb.terminator = ::MIR::Terminator::make_Return({});
        mir_fcn.blocks.push_back(::std::move( bb ));
    }
    else
    {
        TU_MATCH_HDRA( (ty.m_data), {)
        default:
            TODO(sp, "auto Clone for " << ty << " - Unknown and not Copy");
        TU_ARMA(Path, te) {
            // closures are identified by the name starting with 'closure#'
            if( TU_TEST1(te.path.m_data, Generic, .m_path.m_components.back().compare(0, 8, "closure#") == 0) ) {
                const auto& gp = te.path.m_data.as_Generic();
                const auto& str = state.resolve.m_crate.get_struct_by_path(sp, gp.m_path);
                Trans_Params p;
                p.sp = sp;
                p.pp_impl = gp.m_params.clone();
                ::std::vector< ::MIR::Param>   values; values.reserve( str.m_data.as_Tuple().size() );
                for(const auto& fld : str.m_data.as_Tuple())
                {
                    ::HIR::TypeRef  tmp;
                    const auto& ty_m = monomorphise_type_needed(fld.ent) ? (tmp = p.monomorph(state.resolve, fld.ent)) : fld.ent;
                    auto fld_lvalue = ::MIR::LValue::new_Field( ::MIR::LValue::new_Deref(::MIR::LValue::new_Argument(0)), static_cast<unsigned>(values.size()) );
                    values.push_back( clone_field(state, sp, mir_fcn, ty_m, mv$(fld_lvalue)) );
                }
                // Construct the result value
                ::MIR::BasicBlock   bb;
                bb.statements.push_back(::MIR::Statement::make_Assign({
                    ::MIR::LValue::new_Return(),
                    ::MIR::RValue::make_Struct({ gp.clone(), mv$(values) })
                    }));
                bb.terminator = ::MIR::Terminator::make_Return({});
                mir_fcn.blocks.push_back(::std::move( bb ));
            }
            else {
                TODO(sp, "auto Clone for " << ty << " - Unknown and not Copy");
            }
            }
        TU_ARMA(Array, te) {
            ASSERT_BUG(sp, te.size_val < 256, "TODO: Is more than 256 elements sane for auto-generated non-Copy Clone impl? " << ty);
            ::std::vector< ::MIR::Param>   values; values.reserve(te.size_val);
            for(size_t i = 0; i < te.size_val; i ++)
            {
                auto fld_lvalue = ::MIR::LValue::new_Field( ::MIR::LValue::new_Deref(::MIR::LValue::new_Argument(0)), static_cast<unsigned>(values.size()) );
                values.push_back( clone_field(state, sp, mir_fcn, *te.inner, mv$(fld_lvalue)) );
            }
            // Construct the result
            ::MIR::BasicBlock   bb;
            bb.statements.push_back(::MIR::Statement::make_Assign({
                ::MIR::LValue::new_Return(),
                ::MIR::RValue::make_Array({ mv$(values) })
                }));
            bb.terminator = ::MIR::Terminator::make_Return({});
            mir_fcn.blocks.push_back(::std::move( bb ));
            }
        TU_ARMA(Tuple, te) {
            assert(te.size() > 0);

            ::std::vector< ::MIR::Param>   values; values.reserve(te.size());
            // For each field of the tuple, create a clone (either using Copy if posible, or calling Clone::clone)
            for(const auto& subty : te)
            {
                auto fld_lvalue = ::MIR::LValue::new_Field( ::MIR::LValue::new_Deref(::MIR::LValue::new_Argument(0)), static_cast<unsigned>(values.size()) );
                values.push_back( clone_field(state, sp, mir_fcn, subty, mv$(fld_lvalue)) );
            }

            // Construct the result tuple
            ::MIR::BasicBlock   bb;
            bb.statements.push_back(::MIR::Statement::make_Assign({
                ::MIR::LValue::new_Return(),
                ::MIR::RValue::make_Tuple({ mv$(values) })
                }));
            bb.terminator = ::MIR::Terminator::make_Return({});
            mir_fcn.blocks.push_back(::std::move( bb ));
            }
        }
    }

    // Function
    ::HIR::Function fcn {
        /*m_save_code=*/false,
        ::HIR::Linkage {},
        ::HIR::Function::Receiver::BorrowShared,
        /*m_abi=*/ABI_RUST,
        /*m_unsafe =*/false,
        /*m_const=*/false,
        ::HIR::GenericParams {},
        /*m_args=*/::make_vec1(::std::make_pair(
            ::HIR::Pattern( ::HIR::PatternBinding(false, ::HIR::PatternBinding::Type::Move, "self", 0), ::HIR::Pattern::Data::make_Any({}) ),
            ::HIR::TypeRef::new_borrow(::HIR::BorrowType::Shared, ty.clone())
            )),
        /*m_variadic=*/false,
        /*m_return=*/ty.clone(),
        ::HIR::ExprPtr {}
        };
    fcn.m_code.m_mir = ::MIR::FunctionPointer( new ::MIR::Function(mv$(mir_fcn)) );

    // Impl
    ::HIR::TraitImpl    impl;
    impl.m_type = mv$(ty);
    impl.m_methods.insert(::std::make_pair( RcString::new_interned("clone"), ::HIR::TraitImpl::ImplEnt< ::HIR::Function> { false, ::std::move(fcn) } ));

    // Add impl to the crate
    auto& list = state.crate.m_trait_impls[state.lang_Clone].get_list_for_type_mut(impl.m_type);
    list.push_back( box$(impl) );
}

void Trans_AutoImpls(::HIR::Crate& crate, TransList& trans_list)
{
    if( TARGETVER_1_19 )
        return ;

    State   state { crate, trans_list };

    // Generate for all
    for(const auto& ty : trans_list.auto_clone_impls)
    {
        state.done_list.insert( ty.clone() );
        Trans_AutoImpl_Clone(state, ty.clone());
    }

    while( !state.todo_list.empty() )
    {
        auto ty = ::std::move(state.todo_list.front());
        state.todo_list.pop_back();

        Trans_AutoImpl_Clone(state, mv$(ty));
    }

    auto impl_list_it = crate.m_trait_impls.find(state.lang_Clone);
    for(const auto& ty : state.done_list)
    {
        assert(impl_list_it != crate.m_trait_impls.end());
        // TODO: Find a way of turning a set into a vector so items can be erased.

        auto p = ::HIR::Path(ty.clone(), ::HIR::GenericPath(state.lang_Clone), "clone");
        //DEBUG("add_function(" << p << ")");
        auto e = trans_list.add_function(::std::move(p));

        const auto* impl_list = impl_list_it->second.get_list_for_type(ty);
        ASSERT_BUG(Span(), impl_list, "No impl list of Clone for " << ty);
        auto& impl = **::std::find_if( impl_list->begin(), impl_list->end(), [&](const auto& i){ return i->m_type == ty; });
        assert( impl.m_methods.size() == 1 );
        e->ptr = &impl.m_methods.begin()->second.data;
    }
}

<|endoftext|>


<commit_before>/*
  The MIT License (MIT)

  Copyright (c) 2015 Andrea Scarpino <me@andreascarpino.it>

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/

#include "lyricswikiapi.h"

#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QWebElement>
#include <QWebFrame>
#include <QWebPage>
#include <QUrl>
#include <QUrlQuery>

#include "lyric.h"

const static QString API_URL = QStringLiteral("http://lyrics.wikia.com/api.php");

LyricsWikiAPI::LyricsWikiAPI(QObject *parent) :
    QObject(parent)
  , network(new QNetworkAccessManager(this))
{
}

LyricsWikiAPI::~LyricsWikiAPI()
{
    qDeleteAll(lyrics.keys());
    qDeleteAll(lyrics.values());
    delete network;
}

void LyricsWikiAPI::getLyricBypassApi(const QString &artist, const QString &song)
{
    qDebug() << "Requesting lyric bypassing APIs for artist" << artist << ", song" << song;

    QUrl url(QStringLiteral("http://lyrics.wikia.com"));

    QString artNoSpaces(artist);
    artNoSpaces.replace(QChar::Space, "_");
    QString songNoSpaces(song);
    songNoSpaces.replace(QChar::Space, "_");

    url.setPath(QChar::fromLatin1('/') + artNoSpaces + QChar::fromLatin1(':') + songNoSpaces);

    qDebug() << "Requesting lyric page" << url;
    QNetworkRequest req(url);
    QNetworkReply* reply = network->get(req);

    Lyric* lyric = new Lyric();
    lyric->setArtist(artist);
    lyric->setSong(song);
    lyrics.insert(reply, lyric);

    connect(reply, &QNetworkReply::finished, this, &LyricsWikiAPI::onGetLyricPageResult);
}

void LyricsWikiAPI::getLyric(const QString &artist, const QString &song)
{
    qDebug() << "Requesting lyric for artist" << artist << ", song" << song;
    QUrl url(API_URL);

    QString artNoSpaces(artist);
    artNoSpaces.replace(QChar::Space, "_");
    QString songNoSpaces(song);
    songNoSpaces.replace(QChar::Space, "_");

    QUrlQuery query;
    query.addQueryItem(QStringLiteral("func"), QStringLiteral("getSong"));
    query.addQueryItem(QStringLiteral("artist"), artNoSpaces);
    query.addQueryItem(QStringLiteral("song"), songNoSpaces);
    query.addQueryItem(QStringLiteral("fmt"), QStringLiteral("realjson"));
    url.setQuery(query);

    QNetworkRequest req(url);
    QNetworkReply* reply = network->get(req);

    connect(reply, &QNetworkReply::finished, this, &LyricsWikiAPI::onGetLyricResult);
}

void LyricsWikiAPI::onGetLyricResult()
{
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender());

    bool err = false;

    if (reply->error() != QNetworkReply::NoError) {
        qCritical() << "Cannot fetch lyric";
        err = true;
    } else {
        QJsonDocument json = QJsonDocument::fromJson(reply->readAll());
        if (!json.isNull()) {
            qDebug() << "Got lyric:\n" << json;

            QJsonObject jsonObj = json.object();

            if (jsonObj.value("lyrics").toString().compare(QStringLiteral("Not found")) != 0) {
                Lyric* lyric = new Lyric();
                lyric->setArtist(jsonObj.value("artist").toString());
                lyric->setSong(jsonObj.value("song").toString());

                const QUrl url(jsonObj.value("url").toString());
                getLyricText(url, lyric);
            } else {
                qDebug() << "No lyric found";
                err = true;
            }
        } else {
            qCritical() << "Got an invalid JSON!";
            err = true;
        }
    }

    if (err) {
        emit lyricFetched(0, false);
    }

    reply->deleteLater();
}

void LyricsWikiAPI::onGetLyricPageResult()
{
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender());

    bool found = false;
    Lyric* lyric = 0;

    if (reply->error() != QNetworkReply::NoError) {
        qCritical() << "Cannot fetch lyric";
    } else {
        QWebPage page;
        page.mainFrame()->setHtml(reply->readAll());
        QWebElement lyricbox = page.mainFrame()->findFirstElement("div[class=lyricbox]");

        if (lyricbox.isNull()) {
            qCritical() << "Cannot parse HTML page";
        } else {
            lyric = lyrics.take(reply);

            if (!lyric) {
                qCritical() << "Got an invalid lyric object!";
            } else {
                lyric->setText(lyricbox.toPlainText());

                found = true;
            }
        }
    }

    emit lyricFetched(lyric, found);

    reply->deleteLater();
}

void LyricsWikiAPI::getLyricText(const QUrl &url, Lyric *lyric)
{
    qDebug() << "Requesting lyric page" << url;
    QNetworkRequest req(url);
    QNetworkReply* reply = network->get(req);
    lyrics.insert(reply, lyric);

    connect(reply, &QNetworkReply::finished, this, &LyricsWikiAPI::onGetLyricPageResult);
}
<commit_msg>Disable autoload of js and images<commit_after>/*
  The MIT License (MIT)

  Copyright (c) 2015 Andrea Scarpino <me@andreascarpino.it>

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/

#include "lyricswikiapi.h"

#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QWebElement>
#include <QWebFrame>
#include <QWebPage>
#include <QUrl>
#include <QUrlQuery>

#include "lyric.h"

const static QString API_URL = QStringLiteral("http://lyrics.wikia.com/api.php");

LyricsWikiAPI::LyricsWikiAPI(QObject *parent) :
    QObject(parent)
  , network(new QNetworkAccessManager(this))
{
}

LyricsWikiAPI::~LyricsWikiAPI()
{
    qDeleteAll(lyrics.keys());
    qDeleteAll(lyrics.values());
    delete network;
}

void LyricsWikiAPI::getLyricBypassApi(const QString &artist, const QString &song)
{
    qDebug() << "Requesting lyric bypassing APIs for artist" << artist << ", song" << song;

    QUrl url(QStringLiteral("http://lyrics.wikia.com"));

    QString artNoSpaces(artist);
    artNoSpaces.replace(QChar::Space, "_");
    QString songNoSpaces(song);
    songNoSpaces.replace(QChar::Space, "_");

    url.setPath(QChar::fromLatin1('/') + artNoSpaces + QChar::fromLatin1(':') + songNoSpaces);

    qDebug() << "Requesting lyric page" << url;
    QNetworkRequest req(url);
    QNetworkReply* reply = network->get(req);

    Lyric* lyric = new Lyric();
    lyric->setArtist(artist);
    lyric->setSong(song);
    lyrics.insert(reply, lyric);

    connect(reply, &QNetworkReply::finished, this, &LyricsWikiAPI::onGetLyricPageResult);
}

void LyricsWikiAPI::getLyric(const QString &artist, const QString &song)
{
    qDebug() << "Requesting lyric for artist" << artist << ", song" << song;
    QUrl url(API_URL);

    QString artNoSpaces(artist);
    artNoSpaces.replace(QChar::Space, "_");
    QString songNoSpaces(song);
    songNoSpaces.replace(QChar::Space, "_");

    QUrlQuery query;
    query.addQueryItem(QStringLiteral("func"), QStringLiteral("getSong"));
    query.addQueryItem(QStringLiteral("artist"), artNoSpaces);
    query.addQueryItem(QStringLiteral("song"), songNoSpaces);
    query.addQueryItem(QStringLiteral("fmt"), QStringLiteral("realjson"));
    url.setQuery(query);

    QNetworkRequest req(url);
    QNetworkReply* reply = network->get(req);

    connect(reply, &QNetworkReply::finished, this, &LyricsWikiAPI::onGetLyricResult);
}

void LyricsWikiAPI::onGetLyricResult()
{
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender());

    bool err = false;

    if (reply->error() != QNetworkReply::NoError) {
        qCritical() << "Cannot fetch lyric";
        err = true;
    } else {
        QJsonDocument json = QJsonDocument::fromJson(reply->readAll());
        if (!json.isNull()) {
            qDebug() << "Got lyric:\n" << json;

            QJsonObject jsonObj = json.object();

            if (jsonObj.value("lyrics").toString().compare(QStringLiteral("Not found")) != 0) {
                Lyric* lyric = new Lyric();
                lyric->setArtist(jsonObj.value("artist").toString());
                lyric->setSong(jsonObj.value("song").toString());

                const QUrl url(jsonObj.value("url").toString());
                getLyricText(url, lyric);
            } else {
                qDebug() << "No lyric found";
                err = true;
            }
        } else {
            qCritical() << "Got an invalid JSON!";
            err = true;
        }
    }

    if (err) {
        emit lyricFetched(0, false);
    }

    reply->deleteLater();
}

void LyricsWikiAPI::onGetLyricPageResult()
{
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender());

    bool found = false;
    Lyric* lyric = 0;

    if (reply->error() != QNetworkReply::NoError) {
        qCritical() << "Cannot fetch lyric";
    } else {
        QWebPage page;
        page.settings()->setAttribute(QWebSettings::AutoLoadImages, false);
        page.settings()->setAttribute(QWebSettings::JavascriptEnabled, false);
        page.mainFrame()->setHtml(reply->readAll());
        QWebElement lyricbox = page.mainFrame()->findFirstElement("div[class=lyricbox]");

        if (lyricbox.isNull()) {
            qCritical() << "Cannot parse HTML page";
        } else {
            lyric = lyrics.take(reply);

            if (!lyric) {
                qCritical() << "Got an invalid lyric object!";
            } else {
                lyric->setText(lyricbox.toPlainText());

                found = true;
            }
        }
    }

    emit lyricFetched(lyric, found);

    reply->deleteLater();
}

void LyricsWikiAPI::getLyricText(const QUrl &url, Lyric *lyric)
{
    qDebug() << "Requesting lyric page" << url;
    QNetworkRequest req(url);
    QNetworkReply* reply = network->get(req);
    lyrics.insert(reply, lyric);

    connect(reply, &QNetworkReply::finished, this, &LyricsWikiAPI::onGetLyricPageResult);
}
<|endoftext|>