<commit_before>/* Copyright (c) 2006, NIF File Format Library and Tools
All rights reserved.  Please see niflib.h for license. */

//-----------------------------------NOTICE----------------------------------//
// Some of this file is automatically filled in by a Python script.  Only    //
// add custom code in the designated areas or it will be overwritten during  //
// the next update.                                                          //
//-----------------------------------NOTICE----------------------------------//

//--BEGIN FILE HEAD CUSTOM CODE--//
//--END CUSTOM CODE--//

#include "../../include/FixLink.h"
#include "../../include/ObjectRegistry.h"
#include "../../include/NIF_IO.h"
#include "../../include/obj/NiBSplineData.h"
using namespace Niflib;

//Definition of TYPE constant
const Type NiBSplineData::TYPE("NiBSplineData", &NiObject::TYPE );

NiBSplineData::NiBSplineData() : numFloatControlPoints((unsigned int)0), numShortControlPoints((unsigned int)0) {
	//--BEGIN CONSTRUCTOR CUSTOM CODE--//
	//--END CUSTOM CODE--//
}

NiBSplineData::~NiBSplineData() {
	//--BEGIN DESTRUCTOR CUSTOM CODE--//
	//--END CUSTOM CODE--//
}

const Type & NiBSplineData::GetType() const {
	return TYPE;
}

NiObject * NiBSplineData::Create() {
	return new NiBSplineData;
}

void NiBSplineData::Read( istream& in, list<unsigned int> & link_stack, const NifInfo & info ) {
	//--BEGIN PRE-READ CUSTOM CODE--//
	//--END CUSTOM CODE--//

	NiObject::Read( in, link_stack, info );
	NifStream( numFloatControlPoints, in, info );
	floatControlPoints.resize(numFloatControlPoints);
	for (unsigned int i1 = 0; i1 < floatControlPoints.size(); i1++) {
		NifStream( floatControlPoints[i1], in, info );
	};
	NifStream( numShortControlPoints, in, info );
	shortControlPoints.resize(numShortControlPoints);
	for (unsigned int i1 = 0; i1 < shortControlPoints.size(); i1++) {
		NifStream( shortControlPoints[i1], in, info );
	};

	//--BEGIN POST-READ CUSTOM CODE--//
	//--END CUSTOM CODE--//
}

void NiBSplineData::Write( ostream& out, const map<NiObjectRef,unsigned int> & link_map, list<NiObject *> & missing_link_stack, const NifInfo & info ) const {
	//--BEGIN PRE-WRITE CUSTOM CODE--//
	//--END CUSTOM CODE--//

	NiObject::Write( out, link_map, missing_link_stack, info );
	numShortControlPoints = (unsigned int)(shortControlPoints.size());
	numFloatControlPoints = (unsigned int)(floatControlPoints.size());
	NifStream( numFloatControlPoints, out, info );
	for (unsigned int i1 = 0; i1 < floatControlPoints.size(); i1++) {
		NifStream( floatControlPoints[i1], out, info );
	};
	NifStream( numShortControlPoints, out, info );
	for (unsigned int i1 = 0; i1 < shortControlPoints.size(); i1++) {
		NifStream( shortControlPoints[i1], out, info );
	};

	//--BEGIN POST-WRITE CUSTOM CODE--//
	//--END CUSTOM CODE--//
}

std::string NiBSplineData::asString( bool verbose ) const {
	//--BEGIN PRE-STRING CUSTOM CODE--//
	//--END CUSTOM CODE--//

	stringstream out;
	unsigned int array_output_count = 0;
	out << NiObject::asString();
	numShortControlPoints = (unsigned int)(shortControlPoints.size());
	numFloatControlPoints = (unsigned int)(floatControlPoints.size());
	out << "  Num Float Control Points:  " << numFloatControlPoints << endl;
	array_output_count = 0;
	for (unsigned int i1 = 0; i1 < floatControlPoints.size(); i1++) {
		if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {
			out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl;
			break;
		};
		if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {
			break;
		};
		out << "    Float Control Points[" << i1 << "]:  " << floatControlPoints[i1] << endl;
		array_output_count++;
	};
	out << "  Num Short Control Points:  " << numShortControlPoints << endl;
	array_output_count = 0;
	for (unsigned int i1 = 0; i1 < shortControlPoints.size(); i1++) {
		if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {
			out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl;
			break;
		};
		if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {
			break;
		};
		out << "    Short Control Points[" << i1 << "]:  " << shortControlPoints[i1] << endl;
		array_output_count++;
	};
	return out.str();

	//--BEGIN POST-STRING CUSTOM CODE--//
	//--END CUSTOM CODE--//
}

void NiBSplineData::FixLinks( const map<unsigned int,NiObjectRef> & objects, list<unsigned int> & link_stack, list<NiObjectRef> & missing_link_stack, const NifInfo & info ) {
	//--BEGIN PRE-FIXLINKS CUSTOM CODE--//
	//--END CUSTOM CODE--//

	NiObject::FixLinks( objects, link_stack, missing_link_stack, info );

	//--BEGIN POST-FIXLINKS CUSTOM CODE--//
	//--END CUSTOM CODE--//
}

std::list<NiObjectRef> NiBSplineData::GetRefs() const {
	list<Ref<NiObject> > refs;
	refs = NiObject::GetRefs();
	return refs;
}

std::list<NiObject *> NiBSplineData::GetPtrs() const {
	list<NiObject *> ptrs;
	ptrs = NiObject::GetPtrs();
	return ptrs;
}

//--BEGIN MISC CUSTOM CODE--//

vector<float> NiBSplineData::GetFloatControlPoints() const
{
	return floatControlPoints;
}

void NiBSplineData::SetFloatControlPoints( vector<float> value )
{
	this->floatControlPoints.clear();
	this->numFloatControlPoints = value.size();

	for(int i = 0; i < value.size(); i++) {
		this->floatControlPoints.push_back(value[i]);
	}
}

void NiBSplineData::AppendFloatControlPoints( vector<float> value )
{
	this->numFloatControlPoints += value.size();

	for(int i = 0; i < this->floatControlPoints.size(); i++) {
		this->floatControlPoints.push_back(value[i]);
	}
}

int NiBSplineData::GetNumFloatControlPoints()
{
	return this->numFloatControlPoints;
}

vector<float> NiBSplineData::GetFloatControlPointRange(int offset, int count) const
{
	vector<float> value;
	if (offset < 0 || count < 0 || ((offset + count) > int(floatControlPoints.size())))
		throw runtime_error("Invalid offset or count.");
	vector<float>::const_iterator srcbeg = floatControlPoints.begin(), srcend = floatControlPoints.begin();
	std::advance(srcbeg, offset);
	std::advance(srcend, offset + count);
	return vector<float>(srcbeg, srcend);
}

vector<short > NiBSplineData::GetShortControlPoints() const
{
	return shortControlPoints;
}

void NiBSplineData::SetShortControlPoints( vector<short> value )
{
	this->shortControlPoints.clear();
	this->numShortControlPoints = value.size();

	for(int i = 0; i < value.size(); i++) {
		this->shortControlPoints.push_back(value[i]);
	}
}

void NiBSplineData::AppendShortControlPoints( vector<short> value )
{
	this->numShortControlPoints += value.size();

	for(int i = 0; i < value.size(); i++) {
		this->shortControlPoints.push_back(value[i]);
	}
}

int NiBSplineData::GetNumShortControlPoints()
{
	return this->numShortControlPoints;
}

vector<short > NiBSplineData::GetShortControlPointRange(int offset, int count) const
{
   vector<short> value;
   if (offset < 0 || count < 0 || ((offset + count) > int(shortControlPoints.size())))
      throw runtime_error("Invalid offset or count.");
   vector<short>::const_iterator srcbeg = shortControlPoints.begin(), srcend = shortControlPoints.begin();
   std::advance(srcbeg, offset);
   std::advance(srcend, offset + count);
   return vector<short>(srcbeg, srcend);
}

//--END CUSTOM CODE--//
<commit_msg>fixed a bug in the appendfloatcontrolpoints method of the splinedata<commit_after>/* Copyright (c) 2006, NIF File Format Library and Tools
All rights reserved.  Please see niflib.h for license. */

//-----------------------------------NOTICE----------------------------------//
// Some of this file is automatically filled in by a Python script.  Only    //
// add custom code in the designated areas or it will be overwritten during  //
// the next update.                                                          //
//-----------------------------------NOTICE----------------------------------//

//--BEGIN FILE HEAD CUSTOM CODE--//
//--END CUSTOM CODE--//

#include "../../include/FixLink.h"
#include "../../include/ObjectRegistry.h"
#include "../../include/NIF_IO.h"
#include "../../include/obj/NiBSplineData.h"
using namespace Niflib;

//Definition of TYPE constant
const Type NiBSplineData::TYPE("NiBSplineData", &NiObject::TYPE );

NiBSplineData::NiBSplineData() : numFloatControlPoints((unsigned int)0), numShortControlPoints((unsigned int)0) {
	//--BEGIN CONSTRUCTOR CUSTOM CODE--//
	//--END CUSTOM CODE--//
}

NiBSplineData::~NiBSplineData() {
	//--BEGIN DESTRUCTOR CUSTOM CODE--//
	//--END CUSTOM CODE--//
}

const Type & NiBSplineData::GetType() const {
	return TYPE;
}

NiObject * NiBSplineData::Create() {
	return new NiBSplineData;
}

void NiBSplineData::Read( istream& in, list<unsigned int> & link_stack, const NifInfo & info ) {
	//--BEGIN PRE-READ CUSTOM CODE--//
	//--END CUSTOM CODE--//

	NiObject::Read( in, link_stack, info );
	NifStream( numFloatControlPoints, in, info );
	floatControlPoints.resize(numFloatControlPoints);
	for (unsigned int i1 = 0; i1 < floatControlPoints.size(); i1++) {
		NifStream( floatControlPoints[i1], in, info );
	};
	NifStream( numShortControlPoints, in, info );
	shortControlPoints.resize(numShortControlPoints);
	for (unsigned int i1 = 0; i1 < shortControlPoints.size(); i1++) {
		NifStream( shortControlPoints[i1], in, info );
	};

	//--BEGIN POST-READ CUSTOM CODE--//
	//--END CUSTOM CODE--//
}

void NiBSplineData::Write( ostream& out, const map<NiObjectRef,unsigned int> & link_map, list<NiObject *> & missing_link_stack, const NifInfo & info ) const {
	//--BEGIN PRE-WRITE CUSTOM CODE--//
	//--END CUSTOM CODE--//

	NiObject::Write( out, link_map, missing_link_stack, info );
	numShortControlPoints = (unsigned int)(shortControlPoints.size());
	numFloatControlPoints = (unsigned int)(floatControlPoints.size());
	NifStream( numFloatControlPoints, out, info );
	for (unsigned int i1 = 0; i1 < floatControlPoints.size(); i1++) {
		NifStream( floatControlPoints[i1], out, info );
	};
	NifStream( numShortControlPoints, out, info );
	for (unsigned int i1 = 0; i1 < shortControlPoints.size(); i1++) {
		NifStream( shortControlPoints[i1], out, info );
	};

	//--BEGIN POST-WRITE CUSTOM CODE--//
	//--END CUSTOM CODE--//
}

std::string NiBSplineData::asString( bool verbose ) const {
	//--BEGIN PRE-STRING CUSTOM CODE--//
	//--END CUSTOM CODE--//

	stringstream out;
	unsigned int array_output_count = 0;
	out << NiObject::asString();
	numShortControlPoints = (unsigned int)(shortControlPoints.size());
	numFloatControlPoints = (unsigned int)(floatControlPoints.size());
	out << "  Num Float Control Points:  " << numFloatControlPoints << endl;
	array_output_count = 0;
	for (unsigned int i1 = 0; i1 < floatControlPoints.size(); i1++) {
		if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {
			out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl;
			break;
		};
		if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {
			break;
		};
		out << "    Float Control Points[" << i1 << "]:  " << floatControlPoints[i1] << endl;
		array_output_count++;
	};
	out << "  Num Short Control Points:  " << numShortControlPoints << endl;
	array_output_count = 0;
	for (unsigned int i1 = 0; i1 < shortControlPoints.size(); i1++) {
		if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {
			out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl;
			break;
		};
		if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {
			break;
		};
		out << "    Short Control Points[" << i1 << "]:  " << shortControlPoints[i1] << endl;
		array_output_count++;
	};
	return out.str();

	//--BEGIN POST-STRING CUSTOM CODE--//
	//--END CUSTOM CODE--//
}

void NiBSplineData::FixLinks( const map<unsigned int,NiObjectRef> & objects, list<unsigned int> & link_stack, list<NiObjectRef> & missing_link_stack, const NifInfo & info ) {
	//--BEGIN PRE-FIXLINKS CUSTOM CODE--//
	//--END CUSTOM CODE--//

	NiObject::FixLinks( objects, link_stack, missing_link_stack, info );

	//--BEGIN POST-FIXLINKS CUSTOM CODE--//
	//--END CUSTOM CODE--//
}

std::list<NiObjectRef> NiBSplineData::GetRefs() const {
	list<Ref<NiObject> > refs;
	refs = NiObject::GetRefs();
	return refs;
}

std::list<NiObject *> NiBSplineData::GetPtrs() const {
	list<NiObject *> ptrs;
	ptrs = NiObject::GetPtrs();
	return ptrs;
}

//--BEGIN MISC CUSTOM CODE--//

vector<float> NiBSplineData::GetFloatControlPoints() const
{
	return floatControlPoints;
}

void NiBSplineData::SetFloatControlPoints( vector<float> value )
{
	this->floatControlPoints.clear();
	this->numFloatControlPoints = value.size();

	for(int i = 0; i < value.size(); i++) {
		this->floatControlPoints.push_back(value[i]);
	}
}

void NiBSplineData::AppendFloatControlPoints( vector<float> value )
{
	this->numFloatControlPoints += value.size();

	for(int i = 0; i < value.size(); i++) {
		this->floatControlPoints.push_back(value[i]);
	}
}

int NiBSplineData::GetNumFloatControlPoints()
{
	return this->numFloatControlPoints;
}

vector<float> NiBSplineData::GetFloatControlPointRange(int offset, int count) const
{
	vector<float> value;
	if (offset < 0 || count < 0 || ((offset + count) > int(floatControlPoints.size())))
		throw runtime_error("Invalid offset or count.");
	vector<float>::const_iterator srcbeg = floatControlPoints.begin(), srcend = floatControlPoints.begin();
	std::advance(srcbeg, offset);
	std::advance(srcend, offset + count);
	return vector<float>(srcbeg, srcend);
}

vector<short > NiBSplineData::GetShortControlPoints() const
{
	return shortControlPoints;
}

void NiBSplineData::SetShortControlPoints( vector<short> value )
{
	this->shortControlPoints.clear();
	this->numShortControlPoints = value.size();

	for(int i = 0; i < value.size(); i++) {
		this->shortControlPoints.push_back(value[i]);
	}
}

void NiBSplineData::AppendShortControlPoints( vector<short> value )
{
	this->numShortControlPoints += value.size();

	for(int i = 0; i < value.size(); i++) {
		this->shortControlPoints.push_back(value[i]);
	}
}

int NiBSplineData::GetNumShortControlPoints()
{
	return this->numShortControlPoints;
}

vector<short > NiBSplineData::GetShortControlPointRange(int offset, int count) const
{
   vector<short> value;
   if (offset < 0 || count < 0 || ((offset + count) > int(shortControlPoints.size())))
      throw runtime_error("Invalid offset or count.");
   vector<short>::const_iterator srcbeg = shortControlPoints.begin(), srcend = shortControlPoints.begin();
   std::advance(srcbeg, offset);
   std::advance(srcend, offset + count);
   return vector<short>(srcbeg, srcend);
}

//--END CUSTOM CODE--//
<|endoftext|>


<commit_before>#include <miopen/config.h>
#include <miopen/gcn_asm_utils.hpp>
#include <miopen/env.hpp>
#include <miopen/errors.hpp>
#include <cstdlib>
#include <cassert>
#include <fstream>
#include <sstream>
#include <cstdio>
#include <cctype>

#if !defined(_WIN32) && !defined(__APPLE__)
#include <ext/stdio_filebuf.h>
#include <unistd.h>
#include <sys/wait.h>
#include <paths.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif // !defined(_WIN32) && !defined(__APPLE__)

MIOPEN_DECLARE_ENV_VAR(MIOPEN_EXPERIMENTAL_GCN_ASM_PATH)

struct tmp_dir_env { static const char * value() { return "TMPDIR"; }};

#ifndef _WIN32 //Linux or APPLE
class TempFile
{
public:
    TempFile(const std::string& path_template)
        : _path(GetTempDirectoryPath() + "/" + path_template + "-XXXXXX")
    {
        _fd = mkstemp(&_path[0]);
        if (_fd == -1) { MIOPEN_THROW("Error: TempFile: mkstemp()"); }
    }

    ~TempFile()
    {
        const int remove_rc = std::remove(_path.c_str());
        const int close_rc = close(_fd);
        if (remove_rc != 0 || close_rc != 0) {
#ifndef NDEBUG // Be quiet in release versions.
            std::fprintf(stderr, "Error: TempFile: On removal of '%s', remove_rc = %d, close_rc = %d.\n", _path.c_str(), remove_rc, close_rc);
#endif
        }
    }

    inline operator const std::string&() { return _path; }

private:
    std::string _path;
    int _fd;

    static
    const std::string GetTempDirectoryPath()
    {
        const auto path = miopen::GetStringEnv(tmp_dir_env{});
        if (path != nullptr) {
            return path;
        }
#if defined(P_tmpdir)
        return P_tmpdir; // a string literal, if defined.
#elif defined(_PATH_TMP)
        return _PATH_TMP; // a string literal, if defined.
#else
        return "/tmp";
#endif
    }
};
#endif

static std::string CleanupPath(const char * p);
static int ExecuteGcnAssembler(const std::string& p, std::vector<std::string>& args, std::istream* in, std::ostream* out);

#if !defined(_WIN32) && !defined(__APPLE__)
class Pipe
{
public:
    Pipe()
        : _read_side_closed(true)
        , _write_side_closed(true)
    {
    }

    Pipe(Pipe&&) = delete;
    Pipe(Pipe&) = delete;
    Pipe& operator =(Pipe&) = delete;
    ~Pipe() { Close(); }
    void CloseRead() { CloseSide(_read_side, _read_side_closed); }
    void CloseWrite() { CloseSide(_write_side, _write_side_closed); }
    int DupRead(int target_fd) { assert(!_read_side_closed); return dup2(_read_side, target_fd); }
    int DupWrite(int target_fd) { assert(!_write_side_closed); return dup2(_write_side, target_fd); }
    int GetReadFd() { return _read_side; }
    int GetWriteFd() { return _write_side; }

    void Close()
    {
        CloseRead();
        CloseWrite();
    }

    void Open()
    {
        if (pipe(_sides)) { MIOPEN_THROW("Error: pipe()"); }
        _read_side_closed = false;
        _write_side_closed = false;
    }

private:
    union
    {
        int _sides[2];
        struct
        {
            int _read_side;
            int _write_side;
        };
    };

    bool _read_side_closed;
    bool _write_side_closed;

    static void CloseSide(int fd, bool& closed)
    {
        if (closed) { return; }
        if (close(fd)) { std::fprintf(stderr, "Error closing pipe"); }
        closed = true;
    }
};
#endif // !defined(_WIN32) && !defined(__APPLE__)

std::string GetGcnAssemblerPathImpl()
{
    const auto asm_path_env_p = miopen::GetStringEnv(MIOPEN_EXPERIMENTAL_GCN_ASM_PATH{});
    if (asm_path_env_p) {
        return CleanupPath(asm_path_env_p);
    }
#ifdef MIOPEN_AMDGCN_ASSEMBLER // string literal generated by CMake
    return CleanupPath(MIOPEN_AMDGCN_ASSEMBLER);
#else
    return "";
#endif
}

std::string GetGcnAssemblerPath()
{
    static const auto result = GetGcnAssemblerPathImpl();
    return result;
}

bool ValidateGcnAssemblerImpl()
{
#if !defined(_WIN32) && !defined(__APPLE__)
    const auto path = GetGcnAssemblerPath();
    if (path.empty()) { return false; }
    if (!std::ifstream(path).good()) { return false; }

    std::vector<std::string> args({"--version"});
    std::stringstream clang_stdout;
    std::string clang_result_line;
    auto clang_rc = ExecuteGcnAssembler(path, args, nullptr, &clang_stdout);

    if (clang_rc != 0) { return false; }

    std::getline(clang_stdout, clang_result_line);
    if (clang_result_line.find("clang") != std::string::npos) {
        while (!clang_stdout.eof()) {
            std::getline(clang_stdout, clang_result_line);
            if (clang_result_line.find("Target: ") != std::string::npos) {
                return clang_result_line.find("amdgcn") != std::string::npos;
            }
        }
    }
#endif // !defined(_WIN32) && !defined(__APPLE__)
    return false;
}

bool ValidateGcnAssembler()
{
    static bool result = ValidateGcnAssemblerImpl();
    return result;
}

int ExecuteGcnAssembler(const std::string& p, std::vector<std::string>& args, std::istream* in, std::ostream* out)
{
#if !defined(_WIN32) && !defined(__APPLE__)
    Pipe clang_stdin;
    Pipe clang_stdout;

    const auto redirect_stdin = (in != nullptr);
    const auto redirect_stdout = (out != nullptr);

    if (redirect_stdin) { clang_stdin.Open(); }
    if (redirect_stdout) { clang_stdout.Open(); }

    int wstatus;
    pid_t pid = fork();
    if (pid == 0) {
        std::string path(p); // to remove constness
        std::vector<char*> c_args;
        c_args.push_back(&path[0]);
        for (auto& arg : args) {
            c_args.push_back(&arg[0]);
        }
        c_args.push_back(nullptr);

        if (redirect_stdin) {
            if (clang_stdin.DupRead(STDIN_FILENO) == -1) { std::exit(EXIT_FAILURE); }
            clang_stdin.Close();
        }

        if (redirect_stdout) {
            if (clang_stdout.DupWrite(STDOUT_FILENO) == -1) { std::exit(EXIT_FAILURE); }
            clang_stdout.Close();
        }

        clang_stdout.Close();

        execv(path.c_str(), c_args.data());
        std::exit(EXIT_FAILURE);
    }
    else {
        if (pid == -1) { MIOPEN_THROW("Error X-AMDGCN-ASM: fork()"); }

        if (redirect_stdin)
        {
            clang_stdin.CloseRead();
            __gnu_cxx::stdio_filebuf<char> clang_stdin_buffer(clang_stdin.GetWriteFd(), std::ios::out);
            std::ostream clang_stdin_stream(&clang_stdin_buffer);
            clang_stdin_stream << in->rdbuf();
            clang_stdin.CloseWrite();
        }

        if (redirect_stdout)
        {
            clang_stdout.CloseWrite();
            __gnu_cxx::stdio_filebuf<char> clang_stdout_buffer(clang_stdout.GetReadFd(), std::ios::in);
            std::istream clang_stdin_stream(&clang_stdout_buffer);
            *out << clang_stdin_stream.rdbuf();
            clang_stdout.CloseRead();
        }

        if (waitpid(pid, &wstatus, 0) != pid) { MIOPEN_THROW("Error: X-AMDGCN-ASM: waitpid()"); }
    }

    if (WIFEXITED(wstatus)) {
        const int exit_status = WEXITSTATUS(wstatus);
        return exit_status;
    }
    else {
        MIOPEN_THROW("Error: X-AMDGCN-ASM: clang terminated abnormally");
    }
#else
    (void)p;
    (void)args;
    (void)in;
    (void)out;
    return -1;
#endif // !defined(_WIN32) && !defined(__APPLE__)
}

int ExecuteGcnAssembler(std::vector<std::string>& args, std::istream* clang_stdin_content, std::ostream* clang_stdout_content)
{
    auto path = GetGcnAssemblerPath();
    return ExecuteGcnAssembler(path, args, clang_stdin_content, clang_stdout_content);
}

static
std::string CleanupPath(const char * p)
{
    std::string path(p);
    static const char bad[] = "!#$*;<>?@\\^`{|}";
    for (char * c = &path[0]; c < (&path[0] + path.length()); ++c) {
        if (std::iscntrl(*c)) {
            *c = '_';
            continue;
        }
        for (const char * b = &bad[0]; b < (&bad[0] + sizeof(bad) - 1); ++b) {
            if (*b == *c) {
                *c = '_';
                break;
            }
        }
    }
    return path;
}

/*
 * Temporary function which emulates online assembly feature of OpenCL-on-ROCm being developed.
 * Not intended to be used in production code, so error handling is very straghtforward,
 * just catch whatever possible and throw an exception.
 */
void AmdgcnAssemble(std::string& source, const std::string& params)
{
#ifndef _WIN32 //Linux or APPLE
    TempFile outfile("amdgcn-asm-out-XXXXXX");

    std::vector<std::string> args ({
        "-x",
        "assembler",
        "-target",
        "amdgcn--amdhsa",
    });

    {
        std::istringstream iss(params);
        std::string param;
        while (iss >> param) {
            args.push_back(param);
        };
    }
    args.push_back("-");
    args.push_back("-o");
    args.push_back(outfile);

    std::istringstream clang_stdin(source);
    const auto clang_rc = ExecuteGcnAssembler(args, &clang_stdin, nullptr);
    if (clang_rc != 0) MIOPEN_THROW("Assembly error(" + std::to_string(clang_rc) + ")");

    std::ifstream file(outfile, std::ios::binary | std::ios::ate);
    bool outfile_read_failed = false;
    do {
        const auto size = file.tellg();
        if (size == -1) { outfile_read_failed = true; break; }
        source.resize(size, '\0');
        file.seekg(std::ios::beg);
        if (file.fail()) { outfile_read_failed = true; break; }
        if (file.rdbuf()->sgetn(&source[0], size) != size) { outfile_read_failed = true; break; }
    } while (false);
    file.close();
    if (outfile_read_failed) {
        MIOPEN_THROW("Error: X-AMDGCN-ASM: outfile_read_failed");
    }
#else
    (void)source; // -warning
    (void)params; // -warning
    MIOPEN_THROW("Error: X-AMDGCN-ASM: online assembly under Windows is not supported");
#endif //WIN32
}
<commit_msg>fixed mac build errors<commit_after>#include <miopen/config.h>
#include <miopen/gcn_asm_utils.hpp>
#include <miopen/env.hpp>
#include <miopen/errors.hpp>
#include <cstdlib>
#include <cassert>
#include <fstream>
#include <sstream>
#include <cstdio>
#include <cctype>

#ifdef __linux__
#include <ext/stdio_filebuf.h>
#include <unistd.h>
#include <sys/wait.h>
#include <paths.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif // __linux__

MIOPEN_DECLARE_ENV_VAR(MIOPEN_EXPERIMENTAL_GCN_ASM_PATH)

struct tmp_dir_env { static const char * value() { return "TMPDIR"; }};

#ifdef __linux__
class TempFile
{
public:
    TempFile(const std::string& path_template)
        : _path(GetTempDirectoryPath() + "/" + path_template + "-XXXXXX")
    {
        _fd = mkstemp(&_path[0]);
        if (_fd == -1) { MIOPEN_THROW("Error: TempFile: mkstemp()"); }
    }

    ~TempFile()
    {
        const int remove_rc = std::remove(_path.c_str());
        const int close_rc = close(_fd);
        if (remove_rc != 0 || close_rc != 0) {
#ifndef NDEBUG // Be quiet in release versions.
            std::fprintf(stderr, "Error: TempFile: On removal of '%s', remove_rc = %d, close_rc = %d.\n", _path.c_str(), remove_rc, close_rc);
#endif
        }
    }

    inline operator const std::string&() { return _path; }

private:
    std::string _path;
    int _fd;

    static
    const std::string GetTempDirectoryPath()
    {
        const auto path = miopen::GetStringEnv(tmp_dir_env{});
        if (path != nullptr) {
            return path;
        }
#if defined(P_tmpdir)
        return P_tmpdir; // a string literal, if defined.
#elif defined(_PATH_TMP)
        return _PATH_TMP; // a string literal, if defined.
#else
        return "/tmp";
#endif
    }
};
#endif

static std::string CleanupPath(const char * p);
static int ExecuteGcnAssembler(const std::string& p, std::vector<std::string>& args, std::istream* in, std::ostream* out);

#ifdef __linux__
class Pipe
{
public:
    Pipe()
        : _read_side_closed(true)
        , _write_side_closed(true)
    {
    }

    Pipe(Pipe&&) = delete;
    Pipe(Pipe&) = delete;
    Pipe& operator =(Pipe&) = delete;
    ~Pipe() { Close(); }
    void CloseRead() { CloseSide(_read_side, _read_side_closed); }
    void CloseWrite() { CloseSide(_write_side, _write_side_closed); }
    int DupRead(int target_fd) { assert(!_read_side_closed); return dup2(_read_side, target_fd); }
    int DupWrite(int target_fd) { assert(!_write_side_closed); return dup2(_write_side, target_fd); }
    int GetReadFd() { return _read_side; }
    int GetWriteFd() { return _write_side; }

    void Close()
    {
        CloseRead();
        CloseWrite();
    }

    void Open()
    {
        if (pipe(_sides)) { MIOPEN_THROW("Error: pipe()"); }
        _read_side_closed = false;
        _write_side_closed = false;
    }

private:
    union
    {
        int _sides[2];
        struct
        {
            int _read_side;
            int _write_side;
        };
    };

    bool _read_side_closed;
    bool _write_side_closed;

    static void CloseSide(int fd, bool& closed)
    {
        if (closed) { return; }
        if (close(fd)) { std::fprintf(stderr, "Error closing pipe"); }
        closed = true;
    }
};
#endif // __linux__

std::string GetGcnAssemblerPathImpl()
{
    const auto asm_path_env_p = miopen::GetStringEnv(MIOPEN_EXPERIMENTAL_GCN_ASM_PATH{});
    if (asm_path_env_p) {
        return CleanupPath(asm_path_env_p);
    }
#ifdef MIOPEN_AMDGCN_ASSEMBLER // string literal generated by CMake
    return CleanupPath(MIOPEN_AMDGCN_ASSEMBLER);
#else
    return "";
#endif
}

std::string GetGcnAssemblerPath()
{
    static const auto result = GetGcnAssemblerPathImpl();
    return result;
}

bool ValidateGcnAssemblerImpl()
{
#ifdef __linux__
    const auto path = GetGcnAssemblerPath();
    if (path.empty()) { return false; }
    if (!std::ifstream(path).good()) { return false; }

    std::vector<std::string> args({"--version"});
    std::stringstream clang_stdout;
    std::string clang_result_line;
    auto clang_rc = ExecuteGcnAssembler(path, args, nullptr, &clang_stdout);

    if (clang_rc != 0) { return false; }

    std::getline(clang_stdout, clang_result_line);
    if (clang_result_line.find("clang") != std::string::npos) {
        while (!clang_stdout.eof()) {
            std::getline(clang_stdout, clang_result_line);
            if (clang_result_line.find("Target: ") != std::string::npos) {
                return clang_result_line.find("amdgcn") != std::string::npos;
            }
        }
    }
#endif // __linux__
    return false;
}

bool ValidateGcnAssembler()
{
    static bool result = ValidateGcnAssemblerImpl();
    return result;
}

int ExecuteGcnAssembler(const std::string& p, std::vector<std::string>& args, std::istream* in, std::ostream* out)
{
#ifdef __linux__
    Pipe clang_stdin;
    Pipe clang_stdout;

    const auto redirect_stdin = (in != nullptr);
    const auto redirect_stdout = (out != nullptr);

    if (redirect_stdin) { clang_stdin.Open(); }
    if (redirect_stdout) { clang_stdout.Open(); }

    int wstatus;
    pid_t pid = fork();
    if (pid == 0) {
        std::string path(p); // to remove constness
        std::vector<char*> c_args;
        c_args.push_back(&path[0]);
        for (auto& arg : args) {
            c_args.push_back(&arg[0]);
        }
        c_args.push_back(nullptr);

        if (redirect_stdin) {
            if (clang_stdin.DupRead(STDIN_FILENO) == -1) { std::exit(EXIT_FAILURE); }
            clang_stdin.Close();
        }

        if (redirect_stdout) {
            if (clang_stdout.DupWrite(STDOUT_FILENO) == -1) { std::exit(EXIT_FAILURE); }
            clang_stdout.Close();
        }

        clang_stdout.Close();

        execv(path.c_str(), c_args.data());
        std::exit(EXIT_FAILURE);
    }
    else {
        if (pid == -1) { MIOPEN_THROW("Error X-AMDGCN-ASM: fork()"); }

        if (redirect_stdin)
        {
            clang_stdin.CloseRead();
            __gnu_cxx::stdio_filebuf<char> clang_stdin_buffer(clang_stdin.GetWriteFd(), std::ios::out);
            std::ostream clang_stdin_stream(&clang_stdin_buffer);
            clang_stdin_stream << in->rdbuf();
            clang_stdin.CloseWrite();
        }

        if (redirect_stdout)
        {
            clang_stdout.CloseWrite();
            __gnu_cxx::stdio_filebuf<char> clang_stdout_buffer(clang_stdout.GetReadFd(), std::ios::in);
            std::istream clang_stdin_stream(&clang_stdout_buffer);
            *out << clang_stdin_stream.rdbuf();
            clang_stdout.CloseRead();
        }

        if (waitpid(pid, &wstatus, 0) != pid) { MIOPEN_THROW("Error: X-AMDGCN-ASM: waitpid()"); }
    }

    if (WIFEXITED(wstatus)) {
        const int exit_status = WEXITSTATUS(wstatus);
        return exit_status;
    }
    else {
        MIOPEN_THROW("Error: X-AMDGCN-ASM: clang terminated abnormally");
    }
#else
    (void)p;
    (void)args;
    (void)in;
    (void)out;
    return -1;
#endif // __linux__
}

int ExecuteGcnAssembler(std::vector<std::string>& args, std::istream* clang_stdin_content, std::ostream* clang_stdout_content)
{
    auto path = GetGcnAssemblerPath();
    return ExecuteGcnAssembler(path, args, clang_stdin_content, clang_stdout_content);
}

static
std::string CleanupPath(const char * p)
{
    std::string path(p);
    static const char bad[] = "!#$*;<>?@\\^`{|}";
    for (char * c = &path[0]; c < (&path[0] + path.length()); ++c) {
        if (std::iscntrl(*c)) {
            *c = '_';
            continue;
        }
        for (const char * b = &bad[0]; b < (&bad[0] + sizeof(bad) - 1); ++b) {
            if (*b == *c) {
                *c = '_';
                break;
            }
        }
    }
    return path;
}

/*
 * Temporary function which emulates online assembly feature of OpenCL-on-ROCm being developed.
 * Not intended to be used in production code, so error handling is very straghtforward,
 * just catch whatever possible and throw an exception.
 */
void AmdgcnAssemble(std::string& source, const std::string& params)
{
#ifdef __linux__
    TempFile outfile("amdgcn-asm-out-XXXXXX");

    std::vector<std::string> args ({
        "-x",
        "assembler",
        "-target",
        "amdgcn--amdhsa",
    });

    {
        std::istringstream iss(params);
        std::string param;
        while (iss >> param) {
            args.push_back(param);
        };
    }
    args.push_back("-");
    args.push_back("-o");
    args.push_back(outfile);

    std::istringstream clang_stdin(source);
    const auto clang_rc = ExecuteGcnAssembler(args, &clang_stdin, nullptr);
    if (clang_rc != 0) MIOPEN_THROW("Assembly error(" + std::to_string(clang_rc) + ")");

    std::ifstream file(outfile, std::ios::binary | std::ios::ate);
    bool outfile_read_failed = false;
    do {
        const auto size = file.tellg();
        if (size == -1) { outfile_read_failed = true; break; }
        source.resize(size, '\0');
        file.seekg(std::ios::beg);
        if (file.fail()) { outfile_read_failed = true; break; }
        if (file.rdbuf()->sgetn(&source[0], size) != size) { outfile_read_failed = true; break; }
    } while (false);
    file.close();
    if (outfile_read_failed) {
        MIOPEN_THROW("Error: X-AMDGCN-ASM: outfile_read_failed");
    }
#else
    (void)source; // -warning
    (void)params; // -warning
    MIOPEN_THROW("Error: X-AMDGCN-ASM: online assembly under Windows is not supported");
#endif // __linux__
}
<|endoftext|>


<commit_before>#include <ast/all.hh>
#include <ast/new-clone.hh>
#include <ast/parametric-ast.hh>
#include <object/symbols.hh>
#include <parser/ast-factory.hh>
#include <parser/parse.hh>
#include <parser/parse-result.hh>
#include <rewrite/rewrite.hh>

namespace std
{
  ostream&
  operator<< (ostream& o, const parser::case_type& c)
  {
    return o << "/* " << (void*) &c << " */ "
             << "case "
             << libport::deref << c.first
             << " => "
             << libport::deref << c.second;
  }

  ostream&
  operator<< (ostream& o, const parser::cases_type& cs)
  {
    o << "/* " << (void*) &cs << " */ "
      << "{" << endl;
    foreach (const parser::case_type& c, cs)
      o << "  " << c << endl;
    return o << "}";
  }

}

namespace parser
{
  ast::rExp
  ast_at(const yy::location& loc,
         ast::rExp cond,
         ast::rExp at, ast::rExp onleave,
         ast::rExp duration)
  {
    if (!onleave)
      onleave = new ast::Noop(loc, 0);

    if (duration)
    {
      PARAMETRIC_AST(desugar,
                     "var '$at' = persist(%exp:1, %exp:2) |"
                     "at ('$at') %exp:3 onleave %exp:4");

      return exp(desugar % cond % duration % at % onleave);
    }
    else
    {
      PARAMETRIC_AST(desugar,
        "Control.at_(%exp:1, detach(%exp:2), detach(%exp:3))");

      return exp(desugar % cond % at % onleave);
    }
  }

  ast::rExp
  ast_at_event(const ast::loc& loc,
               ast::rExp event, ast::rExp payload,
               ast::rExp at, ast::rExp onleave)
  {
    if (!onleave)
      onleave = new ast::Noop(loc, 0);

    PARAMETRIC_AST(desugar,
      "detach("
      "{"
      "  %exp:1.onEvent(closure ('$at')"
      "  {"
      "    var '$pattern' = Pattern.new(%exp:2) |"
      "    if ('$pattern'.match('$at'.payload))"
      "    {"
      "      %exp: 3"
      "    }"
      "  })"
      "})");

    PARAMETRIC_AST(desugar_body,
      "      %exp:1 |"
      "      waituntil(!'$at'.active) |"
      "      %exp:2");

    ast::rExp body = exp(desugar_body % at % onleave);
    return exp(desugar % event % payload % rewrite::pattern_bind(payload, body));
  }

  ast::rExp
  ast_waituntil_event(const ast::loc& loc,
                      ast::rExp event,
                      ast::exps_type* payload)
  {
    PARAMETRIC_AST
      (desugar,
       "var '$pattern' = Pattern.new(%exp:1) |"
       "%exp:2.'waituntil'('$pattern') |");

    ast::rList d_payload = new ast::List(loc, payload);
    return rewrite::pattern_bind(
      d_payload,
      exp(desugar % d_payload % event),
      false);
  }

  ast::rExp
  ast_whenever_event(const ast::loc& loc,
                     ast::rExp event, ast::rExp payload,
                     ast::rExp body, ast::rExp onleave)
  {
    if (!onleave)
      onleave = new ast::Noop(loc, 0);

    PARAMETRIC_AST(desugar,
      "detach("
      "{"
      "%exp:1.onEvent("
      "  closure ('$whenever')"
      "  {"
      "    var '$pattern' = Pattern.new(%exp:2);"
      "    if ('$pattern'.match('$whenever'.payload))"
      "      detach("
      "        {"
      "          %exp:3"
      "        })"
      "  })"
      "})");

    PARAMETRIC_AST(desugar_body,
      "while (true)"
      "{"
      "  %exp:1 |"
      "  if(!'$whenever'.active)"
      "    break"
      "} |"
      "%exp:2");

    body = exp(desugar_body % body % onleave);

    return exp(desugar % event
               % payload % rewrite::pattern_bind(payload, body));
  }

  /// Create a new Tree node composing \c Lhs and \c Rhs with \c Op.
  /// \param op must be & or |.
  ast::rExp
  ast_bin(const yy::location& l,
          ast::flavor_type op, ast::rExp lhs, ast::rExp rhs)
  {
    ast::rExp res = 0;
    assert (lhs);
    assert (rhs);
    switch (op)
    {
    case ast::flavor_pipe:
    {
      ast::rPipe pipe;
      if (pipe = lhs.unsafe_cast<ast::Pipe>())
        pipe->children_get().push_back(rhs);
      else
      {
        pipe = new ast::Pipe(l, ast::exps_type());
        pipe->children_get().push_back(lhs);
        pipe->children_get().push_back(rhs);
      }
      res = pipe;
      break;
    }
    case ast::flavor_and:
    {
      ast::rAnd rand;
      if (rand = lhs.unsafe_cast<ast::And>())
        rand->children_get().push_back(rhs);
      else
      {
        rand = new ast::And(l, ast::exps_type());
        rand->children_get().push_back(lhs);
        rand->children_get().push_back(rhs);
      }
      res = rand;
      break;
    }
    case ast::flavor_comma:
    case ast::flavor_semicolon:
    {
      ast::rNary nary = new ast::Nary(l);
      nary->push_back(lhs, op);
      nary->push_back(rhs);
      res = nary;
      break;
    }
    case ast::flavor_none:
      pabort(op);
    }
    return res;
  }

  /// "<method> (args)".
  ast::rCall
  ast_call (const yy::location& l,
            libport::Symbol method)
  {
    return ast_call(l, new ast::Implicit(l), method);
  }

  /// "<target> . <method> (args)".
  ast::rCall
  ast_call (const yy::location& l,
            ast::rExp target, libport::Symbol method, ast::exps_type* args)
  {
    return new ast::Call(l, args, target, method);
  }

  /// "<target> . <method> ()".
  ast::rCall
  ast_call(const yy::location& l, ast::rExp target, libport::Symbol method)
  {
    return ast_call(l, target, method, 0);
  }

  /// "<target> . <method> (<arg1>)".
  ast::rCall
  ast_call(const yy::location& l,
           ast::rExp target, libport::Symbol method, ast::rExp arg1)
  {
    ast::rCall res = ast_call(l, target, method, new ast::exps_type);
    res->arguments_get()->push_back(arg1);
    return res;
  }

  /// "<target> . <method> (<arg1>, <arg2>)".
  /// "<target> . <method> (<arg1>, <arg2>, <arg3>)".
  ast::rCall
  ast_call(const yy::location& l,
           ast::rExp target, libport::Symbol method,
           ast::rExp arg1, ast::rExp arg2, ast::rExp arg3)
  {
    ast::rCall res = ast_call(l, target, method, new ast::exps_type);
    res->arguments_get()->push_back(arg1);
    res->arguments_get()->push_back(arg2);
    if (arg3)
      res->arguments_get()->push_back(arg3);
    return res;
  }

  ast::rExp
  ast_closure(ast::rExp value)
  {
    PARAMETRIC_AST(a, "closure () { %exp:1 }");
    return exp(a % value);
  }

  /// Build a for loop.
  // The increment is included directly in the condition to make sure
  // it is executed on `continue'.
  ast::rExp
  ast_for (const yy::location&, ast::flavor_type,
           ast::rExp init, ast::rExp test, ast::rExp inc,
           ast::rExp body)
  {
    // FIXME: for| is handled as a simple for
    PARAMETRIC_AST(desugar,

      "{"
      "  %exp:1 |"
      "  var '$tmp-for-first' = true |"
      "  while ({ if ('$tmp-for-first') '$tmp-for-first' = false else %exp:2 | %exp:3})"
      "    %exp:4 |"
      "}"
      );

    desugar
      % init
      % inc
      % test
      % body;

    return exp(desugar);
  }

  /// Return \a e in a ast::Scope unless it is already one.
  ast::rScope
  ast_scope(const yy::location& l, ast::rExp target, ast::rExp e)
  {
    if (ast::rScope res = e.unsafe_cast<ast::Scope>())
      return res;
    else if (target)
      return new ast::Do(l, e, target);
    else
      return new ast::Scope(l, e);
  }

  ast::rScope
  ast_scope(const yy::location& l, ast::rExp e)
  {
    return ast_scope(l, 0, e);
  }

  ast::rExp
  ast_string(const yy::location& l, libport::Symbol s)
  {
    return new ast::String(l, s);
  }

  ast::rExp
  ast_switch(const yy::location& l, ast::rExp cond,
             const cases_type& cases, ast::rExp def)
  {
    (void) l;

    PARAMETRIC_AST(nil, "nil");
    ast::rExp inner = def ? def : exp(nil);
    rforeach (const case_type& c, cases)
    {
      PARAMETRIC_AST(a,
                     "var '$pattern' = Pattern.new(%exp:1) |"
                     "if ('$pattern'.match('$switch')) %exp:2 else %exp:3");
      ast::rExp body = rewrite::pattern_bind(c.first,
                                             c.second);
      a % c.first
        % body
        % inner;
      inner = ast::exp(a);
    }

    PARAMETRIC_AST(sw, "{ var '$switch' = %exp:1 | %exp:2 }");
    return exp(sw % cond % inner);
  }

  ast::rLValue ast_lvalue_once(const ast::rLValue& lvalue)
  {
    ast::rCall tmp = ast_call(lvalue->location_get(), SYMBOL(DOLLAR_tmp));

    if (lvalue->call()->target_implicit())
      return lvalue.get();
    else
      return ast_call(lvalue->location_get(), tmp, lvalue->call()->name_get());
  }

  ast::rExp ast_lvalue_wrap(const ast::rLValue& lvalue, const ast::rExp& e)
  {
    PARAMETRIC_AST(wrap,
                   "{"
                   "var '$tmp' = %exp:1;"
                   "%exp:2;"
                   "}"
      );

    if (lvalue->call()->target_implicit())
      return e;
    else
    {
      wrap % lvalue->call()->target_get() % e;
      return exp(wrap);
    }
  }

}
<commit_msg>Fix a compile error with gcc3.<commit_after>#include <ast/all.hh>
#include <ast/new-clone.hh>
#include <ast/parametric-ast.hh>
#include <object/symbols.hh>
#include <parser/ast-factory.hh>
#include <parser/parse.hh>
#include <parser/parse-result.hh>
#include <rewrite/rewrite.hh>

namespace std
{
  ostream&
  operator<< (ostream& o, const parser::case_type& c)
  {
    return o << "/* " << (const void*) &c << " */ "
             << "case "
             << libport::deref << c.first
             << " => "
             << libport::deref << c.second;
  }

  ostream&
  operator<< (ostream& o, const parser::cases_type& cs)
  {
    o << "/* " << (const void*) &cs << " */ "
      << "{" << endl;
    foreach (const parser::case_type& c, cs)
      o << "  " << c << endl;
    return o << "}";
  }

}

namespace parser
{
  ast::rExp
  ast_at(const yy::location& loc,
         ast::rExp cond,
         ast::rExp at, ast::rExp onleave,
         ast::rExp duration)
  {
    if (!onleave)
      onleave = new ast::Noop(loc, 0);

    if (duration)
    {
      PARAMETRIC_AST(desugar,
                     "var '$at' = persist(%exp:1, %exp:2) |"
                     "at ('$at') %exp:3 onleave %exp:4");

      return exp(desugar % cond % duration % at % onleave);
    }
    else
    {
      PARAMETRIC_AST(desugar,
        "Control.at_(%exp:1, detach(%exp:2), detach(%exp:3))");

      return exp(desugar % cond % at % onleave);
    }
  }

  ast::rExp
  ast_at_event(const ast::loc& loc,
               ast::rExp event, ast::rExp payload,
               ast::rExp at, ast::rExp onleave)
  {
    if (!onleave)
      onleave = new ast::Noop(loc, 0);

    PARAMETRIC_AST(desugar,
      "detach("
      "{"
      "  %exp:1.onEvent(closure ('$at')"
      "  {"
      "    var '$pattern' = Pattern.new(%exp:2) |"
      "    if ('$pattern'.match('$at'.payload))"
      "    {"
      "      %exp: 3"
      "    }"
      "  })"
      "})");

    PARAMETRIC_AST(desugar_body,
      "      %exp:1 |"
      "      waituntil(!'$at'.active) |"
      "      %exp:2");

    ast::rExp body = exp(desugar_body % at % onleave);
    return exp(desugar % event % payload % rewrite::pattern_bind(payload, body));
  }

  ast::rExp
  ast_waituntil_event(const ast::loc& loc,
                      ast::rExp event,
                      ast::exps_type* payload)
  {
    PARAMETRIC_AST
      (desugar,
       "var '$pattern' = Pattern.new(%exp:1) |"
       "%exp:2.'waituntil'('$pattern') |");

    ast::rList d_payload = new ast::List(loc, payload);
    return rewrite::pattern_bind(
      d_payload,
      exp(desugar % d_payload % event),
      false);
  }

  ast::rExp
  ast_whenever_event(const ast::loc& loc,
                     ast::rExp event, ast::rExp payload,
                     ast::rExp body, ast::rExp onleave)
  {
    if (!onleave)
      onleave = new ast::Noop(loc, 0);

    PARAMETRIC_AST(desugar,
      "detach("
      "{"
      "%exp:1.onEvent("
      "  closure ('$whenever')"
      "  {"
      "    var '$pattern' = Pattern.new(%exp:2);"
      "    if ('$pattern'.match('$whenever'.payload))"
      "      detach("
      "        {"
      "          %exp:3"
      "        })"
      "  })"
      "})");

    PARAMETRIC_AST(desugar_body,
      "while (true)"
      "{"
      "  %exp:1 |"
      "  if(!'$whenever'.active)"
      "    break"
      "} |"
      "%exp:2");

    body = exp(desugar_body % body % onleave);

    return exp(desugar % event
               % payload % rewrite::pattern_bind(payload, body));
  }

  /// Create a new Tree node composing \c Lhs and \c Rhs with \c Op.
  /// \param op must be & or |.
  ast::rExp
  ast_bin(const yy::location& l,
          ast::flavor_type op, ast::rExp lhs, ast::rExp rhs)
  {
    ast::rExp res = 0;
    assert (lhs);
    assert (rhs);
    switch (op)
    {
    case ast::flavor_pipe:
    {
      ast::rPipe pipe;
      if (pipe = lhs.unsafe_cast<ast::Pipe>())
        pipe->children_get().push_back(rhs);
      else
      {
        pipe = new ast::Pipe(l, ast::exps_type());
        pipe->children_get().push_back(lhs);
        pipe->children_get().push_back(rhs);
      }
      res = pipe;
      break;
    }
    case ast::flavor_and:
    {
      ast::rAnd rand;
      if (rand = lhs.unsafe_cast<ast::And>())
        rand->children_get().push_back(rhs);
      else
      {
        rand = new ast::And(l, ast::exps_type());
        rand->children_get().push_back(lhs);
        rand->children_get().push_back(rhs);
      }
      res = rand;
      break;
    }
    case ast::flavor_comma:
    case ast::flavor_semicolon:
    {
      ast::rNary nary = new ast::Nary(l);
      nary->push_back(lhs, op);
      nary->push_back(rhs);
      res = nary;
      break;
    }
    case ast::flavor_none:
      pabort(op);
    }
    return res;
  }

  /// "<method> (args)".
  ast::rCall
  ast_call (const yy::location& l,
            libport::Symbol method)
  {
    return ast_call(l, new ast::Implicit(l), method);
  }

  /// "<target> . <method> (args)".
  ast::rCall
  ast_call (const yy::location& l,
            ast::rExp target, libport::Symbol method, ast::exps_type* args)
  {
    return new ast::Call(l, args, target, method);
  }

  /// "<target> . <method> ()".
  ast::rCall
  ast_call(const yy::location& l, ast::rExp target, libport::Symbol method)
  {
    return ast_call(l, target, method, 0);
  }

  /// "<target> . <method> (<arg1>)".
  ast::rCall
  ast_call(const yy::location& l,
           ast::rExp target, libport::Symbol method, ast::rExp arg1)
  {
    ast::rCall res = ast_call(l, target, method, new ast::exps_type);
    res->arguments_get()->push_back(arg1);
    return res;
  }

  /// "<target> . <method> (<arg1>, <arg2>)".
  /// "<target> . <method> (<arg1>, <arg2>, <arg3>)".
  ast::rCall
  ast_call(const yy::location& l,
           ast::rExp target, libport::Symbol method,
           ast::rExp arg1, ast::rExp arg2, ast::rExp arg3)
  {
    ast::rCall res = ast_call(l, target, method, new ast::exps_type);
    res->arguments_get()->push_back(arg1);
    res->arguments_get()->push_back(arg2);
    if (arg3)
      res->arguments_get()->push_back(arg3);
    return res;
  }

  ast::rExp
  ast_closure(ast::rExp value)
  {
    PARAMETRIC_AST(a, "closure () { %exp:1 }");
    return exp(a % value);
  }

  /// Build a for loop.
  // The increment is included directly in the condition to make sure
  // it is executed on `continue'.
  ast::rExp
  ast_for (const yy::location&, ast::flavor_type,
           ast::rExp init, ast::rExp test, ast::rExp inc,
           ast::rExp body)
  {
    // FIXME: for| is handled as a simple for
    PARAMETRIC_AST(desugar,

      "{"
      "  %exp:1 |"
      "  var '$tmp-for-first' = true |"
      "  while ({ if ('$tmp-for-first') '$tmp-for-first' = false else %exp:2 | %exp:3})"
      "    %exp:4 |"
      "}"
      );

    desugar
      % init
      % inc
      % test
      % body;

    return exp(desugar);
  }

  /// Return \a e in a ast::Scope unless it is already one.
  ast::rScope
  ast_scope(const yy::location& l, ast::rExp target, ast::rExp e)
  {
    if (ast::rScope res = e.unsafe_cast<ast::Scope>())
      return res;
    else if (target)
      return new ast::Do(l, e, target);
    else
      return new ast::Scope(l, e);
  }

  ast::rScope
  ast_scope(const yy::location& l, ast::rExp e)
  {
    return ast_scope(l, 0, e);
  }

  ast::rExp
  ast_string(const yy::location& l, libport::Symbol s)
  {
    return new ast::String(l, s);
  }

  ast::rExp
  ast_switch(const yy::location& l, ast::rExp cond,
             const cases_type& cases, ast::rExp def)
  {
    (void) l;

    PARAMETRIC_AST(nil, "nil");
    ast::rExp inner = def ? def : exp(nil);
    rforeach (const case_type& c, cases)
    {
      PARAMETRIC_AST(a,
                     "var '$pattern' = Pattern.new(%exp:1) |"
                     "if ('$pattern'.match('$switch')) %exp:2 else %exp:3");
      ast::rExp body = rewrite::pattern_bind(c.first,
                                             c.second);
      a % c.first
        % body
        % inner;
      inner = ast::exp(a);
    }

    PARAMETRIC_AST(sw, "{ var '$switch' = %exp:1 | %exp:2 }");
    return exp(sw % cond % inner);
  }

  ast::rLValue ast_lvalue_once(const ast::rLValue& lvalue)
  {
    ast::rCall tmp = ast_call(lvalue->location_get(), SYMBOL(DOLLAR_tmp));

    if (lvalue->call()->target_implicit())
      return lvalue.get();
    else
      return ast_call(lvalue->location_get(), tmp, lvalue->call()->name_get());
  }

  ast::rExp ast_lvalue_wrap(const ast::rLValue& lvalue, const ast::rExp& e)
  {
    PARAMETRIC_AST(wrap,
                   "{"
                   "var '$tmp' = %exp:1;"
                   "%exp:2;"
                   "}"
      );

    if (lvalue->call()->target_implicit())
      return e;
    else
    {
      wrap % lvalue->call()->target_get() % e;
      return exp(wrap);
    }
  }

}
<|endoftext|>


<commit_before>#include "parser.hpp"

namespace Mirb
{
	Tree::Node *Parser::parse_class()
	{
		auto range = capture();

		lexer.step();

		auto result = new (fragment) Tree::ClassNode;

		result->range = range;

		if(matches(Lexeme::LEFT_SHIFT))
		{
			result->singleton = parse_expression();
			result->super = nullptr;
			result->name = nullptr;
		}
		else
		{
			result->singleton = nullptr;

			parse_module_name(*result);

			if(lexeme() == Lexeme::LESS)
			{
				lexer.step();

				result->super = parse_expression();
			}
			else
				result->super = nullptr;
		}

		lexer.lexeme.prev_set(range);

		parse_sep();

		allocate_scope(Tree::Scope::Class, [&]  {
			result->scope = scope;
			result->scope->group = parse_group();
		});

		close_pair("class", *range, Lexeme::KW_END);

		return result;
	}

	void Parser::parse_module_name(Tree::ModuleNode &node)
	{
		auto range = capture();

		Tree::Node *result = typecheck(parse_unary());

		lexer.lexeme.prev_set(range);

		node.name = nullptr;
		node.scoped = nullptr;

		if(!result)
			return;

		if(result->type() != Tree::Node::Constant)
		{
			report(*range, "Expected a constant");

			return;
		}

		auto constant = (Tree::ConstantNode *)result;

		node.top_scope = constant->top_scope;
		node.scoped = constant->obj;
		node.name = constant->name;
	}

	Tree::Node *Parser::parse_module()
	{
		auto range = capture();

		lexer.step();

		auto result = new (fragment) Tree::ModuleNode;

		result->range = range;

		parse_module_name(*result);

		lexer.lexeme.prev_set(range);

		allocate_scope(Tree::Scope::Module, [&]  {
			result->scope = scope;
			result->scope->group = parse_group();
		});

		close_pair("module", *range, Lexeme::KW_END);

		return result;
	}

	bool Parser::is_parameter()
	{
		switch(lexeme())
		{
			case Lexeme::PARENT_OPEN:
			case Lexeme::AMPERSAND:
			case Lexeme::MUL:
			case Lexeme::IDENT:
				return true;

			default:
				return false;
		}
	}

	void Parser::parse_parameter(bool block, bool nested)
	{
		if(lexeme() == Lexeme::PARENT_OPEN)
		{
			SourceLoc parent = lexer.lexeme;

			lexer.step();

			SourceLoc range;

			auto group = parse_multiple_expressions(true, false);

			lexer.lexeme.prev_set(&range);

			process_lhs(group, range, true);

			auto parameter = scope->alloc_var<Tree::Parameter>();

			parameter->range = range;
			parameter->node = group;
			parameter->parameter_group = true;

			scope->parameters.push(parameter);

			close_pair("parameter group", parent, Lexeme::PARENT_CLOSE);

			return;
		}

		SourceLoc range = lexer.lexeme;

		bool block_parameter = matches(Lexeme::AMPERSAND);
		bool array_parameter = matches(Lexeme::MUL);

		if(block_parameter && array_parameter)
			error("A parameter cannot be both the block and the array parameter");

		Tree::Parameter *parameter = nullptr;

		if(lexeme() == Lexeme::IDENT)
		{
			Symbol *symbol = lexer.lexeme.symbol;
			range.expand(lexer.lexeme);

			if(scope->defined(symbol, false))
				error("Variable " + lexer.lexeme.string() + " already defined");
			else
			{
				parameter = scope->define<Tree::Parameter>(symbol);
				parameter->range = range;
			}

			lexer.step();
		}
		else if(array_parameter)
		{
			parameter = scope->alloc_var<Tree::Parameter>();
			parameter->range = range;
			parameter->discard = true;
		}
		else
			error("Expected a parameter name");

		if(scope->array_parameter && !scope->array_parameter->reported && !block_parameter)
		{
			scope->array_parameter->reported = true;
			report(scope->array_parameter->range, "Array parameters must be last in the parameter list or right before a block parameter");
		}

		if(scope->block_parameter && !scope->block_parameter->reported)
		{
			scope->block_parameter->reported = true;
			report(scope->block_parameter->range, "Block parameters must be last in the parameter list");
		}

		if(matches(Lexeme::ASSIGN))
		{
			auto node = parse_operator_expression(false);

			if(parameter)
			{
				parameter->node = node;
				parameter->has_default_value = true;
			}

			if(array_parameter || block_parameter)
				report(range, (array_parameter ? "Array" : "Block") + std::string(" parameters cannot have default values"));
		}
		else if(!array_parameter && !block_parameter)
		{
			auto prev = scope->parameters.find([&](Tree::Parameter *param) -> bool { return param->has_default_value; }, nullptr);

			if(prev && !prev->reported)
			{
				prev->reported = true;
				report(prev->range, "Parameters with default values must come after regular parameters");
			}
		}

		if(array_parameter)
		{
			if(scope->array_parameter)
				report(range, "You can only receive the remaining arguments in one parameter");
			else
				scope->array_parameter = parameter;
		}
		else if(block_parameter)
		{
			if(scope->block_parameter)
				report(range, "You can only receive the block in one parameter");
			else
				scope->block_parameter = parameter;
		}
		else if (parameter)
			scope->parameters.push(parameter);
	}

	void Parser::parse_parameters(bool block, bool nested)
	{
		parse_parameter(block, nested);

		while(lexeme() == Lexeme::COMMA)
		{
			SourceLoc comma = lexer.lexeme;

			step_lines();

			if(lexeme() == Lexeme::COMMA)
			{
				SourceLoc error;

				while(lexeme() == Lexeme::COMMA)
				{
					step_lines();
				}

				report(error, "Unexpected multiple commas");
			}

			if(is_parameter())
				parse_parameter(block, nested);
			else
			{
				auto parameter = scope->alloc_var<Tree::Parameter>();

				parameter->range = comma;
				parameter->discard = true;

				if(scope->array_parameter)
					report(comma, "Cannot have a trailing comma and an array parameter");
				else
					scope->array_parameter = parameter;
			}
		}
	}

	Tree::Node *Parser::parse_method()
	{
		lexer.lexeme.allow_keywords = false;

		auto range = capture();

		lexer.step();

		lexer.lexeme.allow_keywords = true;

		auto result = new (fragment) Tree::MethodNode;

		result->range = range;

		Symbol *symbol;

		switch(lexeme())
		{
			case Lexeme::PARENT_OPEN:
			{
				lexer.step();

				skip_lines();

				if(lexeme() == Lexeme::PARENT_CLOSE)
					error("Expected expression");
				else
					result->singleton = parse_expression();

				match(Lexeme::PARENT_CLOSE);

				match(Lexeme::DOT);
			}
			break;

			case Lexeme::CVAR:
			{
				result->singleton = new (fragment) Tree::CVarNode(lexer.lexeme.symbol);

				lexer.step();

				match(Lexeme::DOT);
			}
			break;

			case Lexeme::IVAR:
			{
				result->singleton = new (fragment) Tree::IVarNode(lexer.lexeme.symbol);

				lexer.step();

				match(Lexeme::DOT);
			}
			break;

			case Lexeme::GLOBAL:
			{
				result->singleton = new (fragment) Tree::GlobalNode(lexer.lexeme.symbol);

				lexer.step();

				match(Lexeme::DOT);
			}
			break;

			case Lexeme::IDENT:
			{
				SourceLoc *range = parse_method_name(symbol);

				if(lexeme() == Lexeme::DOT)
				{
					if(symbol == Symbol::get("self"))
						result->singleton = new (fragment) Tree::SelfNode;
					else
						result->singleton = parse_variable(symbol, range);

					lexer.step();
				}
				else
				{
					result->singleton = nullptr;
					goto skip_name;
				}
			}
			break;

			default:
				result->singleton = nullptr;
				break;
		}

		parse_method_name(symbol);
skip_name:

		result->name = symbol;

		lexer.lexeme.prev_set(result->range);

		allocate_scope(Tree::Scope::Method, [&] {
			result->scope = scope;
			scope->owner = scope;
			scope->range = result->range;

			if(lexeme() == Lexeme::PARENT_OPEN)
			{
				SourceLoc parent_open = lexer.lexeme;

				lexer.step();

				skip_lines();

				if(lexeme() != Lexeme::PARENT_CLOSE)
					parse_parameters(false);

				close_pair("method parameters", parent_open, Lexeme::PARENT_CLOSE);
			}
			else
			{
				if(is_parameter())
					parse_parameters(false);

				parse_sep();
			}

			Tree::Node *node;

			Tree::VoidTrapper *trapper = trap([&]{
				node = parse_group();
			});

			result->scope->group = parse_exception_handlers(node, trapper);
		});

		close_pair("method", *range, Lexeme::KW_END);

		return result;
	}
};
<commit_msg>Allow array parameters to be anywhere.<commit_after>#include "parser.hpp"

namespace Mirb
{
	Tree::Node *Parser::parse_class()
	{
		auto range = capture();

		lexer.step();

		auto result = new (fragment) Tree::ClassNode;

		result->range = range;

		if(matches(Lexeme::LEFT_SHIFT))
		{
			result->singleton = parse_expression();
			result->super = nullptr;
			result->name = nullptr;
		}
		else
		{
			result->singleton = nullptr;

			parse_module_name(*result);

			if(lexeme() == Lexeme::LESS)
			{
				lexer.step();

				result->super = parse_expression();
			}
			else
				result->super = nullptr;
		}

		lexer.lexeme.prev_set(range);

		parse_sep();

		allocate_scope(Tree::Scope::Class, [&]  {
			result->scope = scope;
			result->scope->group = parse_group();
		});

		close_pair("class", *range, Lexeme::KW_END);

		return result;
	}

	void Parser::parse_module_name(Tree::ModuleNode &node)
	{
		auto range = capture();

		Tree::Node *result = typecheck(parse_unary());

		lexer.lexeme.prev_set(range);

		node.name = nullptr;
		node.scoped = nullptr;

		if(!result)
			return;

		if(result->type() != Tree::Node::Constant)
		{
			report(*range, "Expected a constant");

			return;
		}

		auto constant = (Tree::ConstantNode *)result;

		node.top_scope = constant->top_scope;
		node.scoped = constant->obj;
		node.name = constant->name;
	}

	Tree::Node *Parser::parse_module()
	{
		auto range = capture();

		lexer.step();

		auto result = new (fragment) Tree::ModuleNode;

		result->range = range;

		parse_module_name(*result);

		lexer.lexeme.prev_set(range);

		allocate_scope(Tree::Scope::Module, [&]  {
			result->scope = scope;
			result->scope->group = parse_group();
		});

		close_pair("module", *range, Lexeme::KW_END);

		return result;
	}

	bool Parser::is_parameter()
	{
		switch(lexeme())
		{
			case Lexeme::PARENT_OPEN:
			case Lexeme::AMPERSAND:
			case Lexeme::MUL:
			case Lexeme::IDENT:
				return true;

			default:
				return false;
		}
	}

	void Parser::parse_parameter(bool block, bool nested)
	{
		if(lexeme() == Lexeme::PARENT_OPEN)
		{
			SourceLoc parent = lexer.lexeme;

			lexer.step();

			SourceLoc range;

			auto group = parse_multiple_expressions(true, false);

			lexer.lexeme.prev_set(&range);

			process_lhs(group, range, true);

			auto parameter = scope->alloc_var<Tree::Parameter>();

			parameter->range = range;
			parameter->node = group;
			parameter->parameter_group = true;

			scope->parameters.push(parameter);

			close_pair("parameter group", parent, Lexeme::PARENT_CLOSE);

			return;
		}

		SourceLoc range = lexer.lexeme;

		bool block_parameter = matches(Lexeme::AMPERSAND);
		bool array_parameter = matches(Lexeme::MUL);

		if(block_parameter && array_parameter)
			error("A parameter cannot be both the block and the array parameter");

		Tree::Parameter *parameter = nullptr;

		if(lexeme() == Lexeme::IDENT)
		{
			Symbol *symbol = lexer.lexeme.symbol;
			range.expand(lexer.lexeme);

			if(scope->defined(symbol, false))
				error("Variable " + lexer.lexeme.string() + " already defined");
			else
			{
				parameter = scope->define<Tree::Parameter>(symbol);
				parameter->range = range;
			}

			lexer.step();
		}
		else if(array_parameter)
		{
			parameter = scope->alloc_var<Tree::Parameter>();
			parameter->range = range;
			parameter->discard = true;
		}
		else
			error("Expected a parameter name");

		if(scope->block_parameter && !scope->block_parameter->reported)
		{
			scope->block_parameter->reported = true;
			report(scope->block_parameter->range, "Block parameters must be last in the parameter list");
		}

		if(matches(Lexeme::ASSIGN))
		{
			auto node = parse_operator_expression(false);

			if(parameter)
			{
				parameter->node = node;
				parameter->has_default_value = true;
			}

			if(array_parameter || block_parameter)
				report(range, (array_parameter ? "Array" : "Block") + std::string(" parameters cannot have default values"));
		}
		else if(!array_parameter && !block_parameter)
		{
			auto prev = scope->parameters.find([&](Tree::Parameter *param) -> bool { return param->has_default_value; }, nullptr);

			if(prev && !prev->reported)
			{
				prev->reported = true;
				report(prev->range, "Parameters with default values must come after regular parameters");
			}
		}

		if(array_parameter)
		{
			if(scope->array_parameter)
				report(range, "You can only receive the remaining arguments in one parameter");
			else
				scope->array_parameter = parameter;
		}
		else if(block_parameter)
		{
			if(scope->block_parameter)
				report(range, "You can only receive the block in one parameter");
			else
				scope->block_parameter = parameter;
		}
		else if (parameter)
			scope->parameters.push(parameter);
	}

	void Parser::parse_parameters(bool block, bool nested)
	{
		parse_parameter(block, nested);

		while(lexeme() == Lexeme::COMMA)
		{
			SourceLoc comma = lexer.lexeme;

			step_lines();

			if(lexeme() == Lexeme::COMMA)
			{
				SourceLoc error;

				while(lexeme() == Lexeme::COMMA)
				{
					step_lines();
				}

				report(error, "Unexpected multiple commas");
			}

			if(is_parameter())
				parse_parameter(block, nested);
			else
			{
				auto parameter = scope->alloc_var<Tree::Parameter>();

				parameter->range = comma;
				parameter->discard = true;

				if(scope->array_parameter)
					report(comma, "Cannot have a trailing comma and an array parameter");
				else
					scope->array_parameter = parameter;
			}
		}
	}

	Tree::Node *Parser::parse_method()
	{
		lexer.lexeme.allow_keywords = false;

		auto range = capture();

		lexer.step();

		lexer.lexeme.allow_keywords = true;

		auto result = new (fragment) Tree::MethodNode;

		result->range = range;

		Symbol *symbol;

		switch(lexeme())
		{
			case Lexeme::PARENT_OPEN:
			{
				lexer.step();

				skip_lines();

				if(lexeme() == Lexeme::PARENT_CLOSE)
					error("Expected expression");
				else
					result->singleton = parse_expression();

				match(Lexeme::PARENT_CLOSE);

				match(Lexeme::DOT);
			}
			break;

			case Lexeme::CVAR:
			{
				result->singleton = new (fragment) Tree::CVarNode(lexer.lexeme.symbol);

				lexer.step();

				match(Lexeme::DOT);
			}
			break;

			case Lexeme::IVAR:
			{
				result->singleton = new (fragment) Tree::IVarNode(lexer.lexeme.symbol);

				lexer.step();

				match(Lexeme::DOT);
			}
			break;

			case Lexeme::GLOBAL:
			{
				result->singleton = new (fragment) Tree::GlobalNode(lexer.lexeme.symbol);

				lexer.step();

				match(Lexeme::DOT);
			}
			break;

			case Lexeme::IDENT:
			{
				SourceLoc *range = parse_method_name(symbol);

				if(lexeme() == Lexeme::DOT)
				{
					if(symbol == Symbol::get("self"))
						result->singleton = new (fragment) Tree::SelfNode;
					else
						result->singleton = parse_variable(symbol, range);

					lexer.step();
				}
				else
				{
					result->singleton = nullptr;
					goto skip_name;
				}
			}
			break;

			default:
				result->singleton = nullptr;
				break;
		}

		parse_method_name(symbol);
skip_name:

		result->name = symbol;

		lexer.lexeme.prev_set(result->range);

		allocate_scope(Tree::Scope::Method, [&] {
			result->scope = scope;
			scope->owner = scope;
			scope->range = result->range;

			if(lexeme() == Lexeme::PARENT_OPEN)
			{
				SourceLoc parent_open = lexer.lexeme;

				lexer.step();

				skip_lines();

				if(lexeme() != Lexeme::PARENT_CLOSE)
					parse_parameters(false);

				close_pair("method parameters", parent_open, Lexeme::PARENT_CLOSE);
			}
			else
			{
				if(is_parameter())
					parse_parameters(false);

				parse_sep();
			}

			Tree::Node *node;

			Tree::VoidTrapper *trapper = trap([&]{
				node = parse_group();
			});

			result->scope->group = parse_exception_handlers(node, trapper);
		});

		close_pair("method", *range, Lexeme::KW_END);

		return result;
	}
};
<|endoftext|>


<commit_before>//
// Copyright Copyright 2009-2022, AMT – The Association For Manufacturing Technology (“AMT”)
// All rights reserved.
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
//

#pragma once

#include "pipeline_context.hpp"
#include "pipeline_contract.hpp"
#include "transform.hpp"
#include <future>

namespace mtconnect {
  class Agent;
  namespace device_model {
    class Device;
  }
  namespace asset {
    class Asset;
    using AssetPtr = std::shared_ptr<Asset>;
  }  // namespace asset

  namespace source::adapter {
    class Adapter;
  }  // namespace source::adapter
  namespace pipeline {
    class Pipeline
    {
    public:
      using Splice = std::function<void(Pipeline *)>;

      Pipeline(PipelineContextPtr context, boost::asio::io_context::strand &st)
        : m_start(std::make_shared<Start>()), m_context(context), m_strand(st)
      {}
      virtual ~Pipeline() { m_start->stop(); }
      virtual void build(const ConfigOptions &options) = 0;
      bool started() const { return m_started; }
      boost::asio::io_context::strand &getStrand() { return m_strand; }

      void applySplices()
      {
        for (auto &splice : m_splices)
        {
          splice(this);
        }
      }

      void clear()
      {
        using namespace std::chrono_literals;
        if (m_start->getNext().size() > 0)
        {
          std::promise<void> p;
          auto f = p.get_future();
          m_strand.dispatch([this, &p]() {
            m_start->stop();
            m_started = false;
            m_start->clear();
            m_start = std::make_shared<Start>();
            p.set_value();
          });

          while (f.wait_for(1ms) != std::future_status::ready)
          {
            m_strand.context().run_for(10ms);
          }
          f.get();
        }
      }

      virtual void start()
      {
        if (m_start)
        {
          m_start->start(m_strand);
          m_started = true;
        }
      }

      bool spliceBefore(const std::string &target, TransformPtr transform, bool reapplied = false)
      {
        Transform::ListOfTransforms xforms;
        m_start->find(target, xforms);
        if (xforms.empty())
          return false;

        transform->unlink();
        for (auto &pair : xforms)
        {
          pair.first->spliceBefore(pair.second, transform);
        }

        if (!reapplied)
        {
          m_splices.emplace_back(
              [target, transform](Pipeline *pipe) { pipe->spliceBefore(target, transform, true); });
        }

        return true;
      }
      bool spliceAfter(const std::string &target, TransformPtr transform, bool reapplied = false)
      {
        Transform::ListOfTransforms xforms;
        m_start->find(target, xforms);
        if (xforms.empty())
          return false;

        transform->unlink();
        for (auto &pair : xforms)
        {
          pair.second->spliceAfter(transform);
        }

        if (!reapplied)
        {
          m_splices.emplace_back(
              [target, transform](Pipeline *pipe) { pipe->spliceAfter(target, transform, true); });
        }

        return true;
      }
      bool firstAfter(const std::string &target, TransformPtr transform, bool reapplied = false)
      {
        Transform::ListOfTransforms xforms;
        m_start->find(target, xforms);
        if (xforms.empty())
          return false;

        for (auto &pair : xforms)
        {
          pair.second->firstAfter(transform);
        }

        if (!reapplied)
        {
          m_splices.emplace_back(
              [target, transform](Pipeline *pipe) { pipe->firstAfter(target, transform, true); });
        }
        return true;
      }

      bool lastAfter(const std::string &target, TransformPtr transform, bool reapplied = false)
      {
        Transform::ListOfTransforms xforms;
        m_start->find(target, xforms);
        if (xforms.empty())
          return false;

        for (auto &pair : xforms)
        {
          pair.second->bind(transform);
        }

        if (!reapplied)
        {
          m_splices.emplace_back(
              [target, transform](Pipeline *pipe) { pipe->lastAfter(target, transform, true); });
        }
        return true;
      }

      bool replace(const std::string &target, TransformPtr transform, bool reapplied = false)
      {
        Transform::ListOfTransforms xforms;
        m_start->find(target, xforms);
        if (xforms.empty())
          return false;

        transform->unlink();
        for (auto &pair : xforms)
        {
          pair.first->replace(pair.second, transform);
        }

        if (!reapplied)
        {
          m_splices.emplace_back(
              [target, transform](Pipeline *pipe) { pipe->replace(target, transform, true); });
        }

        return true;
      }

      bool remove(const std::string &target)
      {
        Transform::ListOfTransforms xforms;
        m_start->find(target, xforms);
        if (xforms.empty())
          return false;

        for (auto &pair : xforms)
        {
          pair.first->remove(pair.second);
        }

        m_splices.emplace_back([target](Pipeline *pipe) { pipe->remove(target); });

        return true;
      }

      const entity::EntityPtr run(const entity::EntityPtr entity) { return m_start->next(entity); }

      TransformPtr bind(TransformPtr transform)
      {
        m_start->bind(transform);
        return transform;
      }

      bool hasContext() const { return bool(m_context); }
      bool hasContract() const { return bool(m_context) && bool(m_context->m_contract); }
      PipelineContextPtr getContext() { return m_context; }
      const auto &getContract() { return m_context->m_contract; }

    protected:
      class Start : public Transform
      {
      public:
        Start() : Transform("Start")
        {
          m_guard = [](const entity::EntityPtr entity) { return SKIP; };
        }
        ~Start() override = default;

        const entity::EntityPtr operator()(const entity::EntityPtr entity) override
        {
          return entity::EntityPtr();
        }
      };

      bool m_started {false};
      TransformPtr m_start;
      PipelineContextPtr m_context;
      boost::asio::io_context::strand m_strand;
      std::list<Splice> m_splices;
    };
  }  // namespace pipeline
}  // namespace mtconnect
<commit_msg>Fixed pipeline shutdown when io context shutdown<commit_after>//
// Copyright Copyright 2009-2022, AMT – The Association For Manufacturing Technology (“AMT”)
// All rights reserved.
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
//

#pragma once

#include "pipeline_context.hpp"
#include "pipeline_contract.hpp"
#include "transform.hpp"
#include <future>

namespace mtconnect {
  class Agent;
  namespace device_model {
    class Device;
  }
  namespace asset {
    class Asset;
    using AssetPtr = std::shared_ptr<Asset>;
  }  // namespace asset

  namespace source::adapter {
    class Adapter;
  }  // namespace source::adapter
  namespace pipeline {
    class Pipeline
    {
    public:
      using Splice = std::function<void(Pipeline *)>;

      Pipeline(PipelineContextPtr context, boost::asio::io_context::strand &st)
        : m_start(std::make_shared<Start>()), m_context(context), m_strand(st)
      {}
      virtual ~Pipeline() { m_start->stop(); }
      virtual void build(const ConfigOptions &options) = 0;
      bool started() const { return m_started; }
      boost::asio::io_context::strand &getStrand() { return m_strand; }

      void applySplices()
      {
        for (auto &splice : m_splices)
        {
          splice(this);
        }
      }

      void clear()
      {
        using namespace std::chrono_literals;
        if (m_start->getNext().size() > 0)
        {
          if (m_strand.context().stopped())
          {
            clearTransforms();
          }
          else
          {
            std::promise<void> p;
            auto f = p.get_future();
            m_strand.dispatch([this, &p]() {
              clearTransforms();
              p.set_value();
            });

            while (f.wait_for(1ms) != std::future_status::ready)
            {
              m_strand.context().run_for(10ms);
            }
            f.get();
          }
        }
      }

      virtual void start()
      {
        if (m_start)
        {
          m_start->start(m_strand);
          m_started = true;
        }
      }

      bool spliceBefore(const std::string &target, TransformPtr transform, bool reapplied = false)
      {
        Transform::ListOfTransforms xforms;
        m_start->find(target, xforms);
        if (xforms.empty())
          return false;

        transform->unlink();
        for (auto &pair : xforms)
        {
          pair.first->spliceBefore(pair.second, transform);
        }

        if (!reapplied)
        {
          m_splices.emplace_back(
              [target, transform](Pipeline *pipe) { pipe->spliceBefore(target, transform, true); });
        }

        return true;
      }
      bool spliceAfter(const std::string &target, TransformPtr transform, bool reapplied = false)
      {
        Transform::ListOfTransforms xforms;
        m_start->find(target, xforms);
        if (xforms.empty())
          return false;

        transform->unlink();
        for (auto &pair : xforms)
        {
          pair.second->spliceAfter(transform);
        }

        if (!reapplied)
        {
          m_splices.emplace_back(
              [target, transform](Pipeline *pipe) { pipe->spliceAfter(target, transform, true); });
        }

        return true;
      }
      bool firstAfter(const std::string &target, TransformPtr transform, bool reapplied = false)
      {
        Transform::ListOfTransforms xforms;
        m_start->find(target, xforms);
        if (xforms.empty())
          return false;

        for (auto &pair : xforms)
        {
          pair.second->firstAfter(transform);
        }

        if (!reapplied)
        {
          m_splices.emplace_back(
              [target, transform](Pipeline *pipe) { pipe->firstAfter(target, transform, true); });
        }
        return true;
      }

      bool lastAfter(const std::string &target, TransformPtr transform, bool reapplied = false)
      {
        Transform::ListOfTransforms xforms;
        m_start->find(target, xforms);
        if (xforms.empty())
          return false;

        for (auto &pair : xforms)
        {
          pair.second->bind(transform);
        }

        if (!reapplied)
        {
          m_splices.emplace_back(
              [target, transform](Pipeline *pipe) { pipe->lastAfter(target, transform, true); });
        }
        return true;
      }

      bool replace(const std::string &target, TransformPtr transform, bool reapplied = false)
      {
        Transform::ListOfTransforms xforms;
        m_start->find(target, xforms);
        if (xforms.empty())
          return false;

        transform->unlink();
        for (auto &pair : xforms)
        {
          pair.first->replace(pair.second, transform);
        }

        if (!reapplied)
        {
          m_splices.emplace_back(
              [target, transform](Pipeline *pipe) { pipe->replace(target, transform, true); });
        }

        return true;
      }

      bool remove(const std::string &target)
      {
        Transform::ListOfTransforms xforms;
        m_start->find(target, xforms);
        if (xforms.empty())
          return false;

        for (auto &pair : xforms)
        {
          pair.first->remove(pair.second);
        }

        m_splices.emplace_back([target](Pipeline *pipe) { pipe->remove(target); });

        return true;
      }

      const entity::EntityPtr run(const entity::EntityPtr entity) { return m_start->next(entity); }

      TransformPtr bind(TransformPtr transform)
      {
        m_start->bind(transform);
        return transform;
      }

      bool hasContext() const { return bool(m_context); }
      bool hasContract() const { return bool(m_context) && bool(m_context->m_contract); }
      PipelineContextPtr getContext() { return m_context; }
      const auto &getContract() { return m_context->m_contract; }

    protected:
      class Start : public Transform
      {
      public:
        Start() : Transform("Start")
        {
          m_guard = [](const entity::EntityPtr entity) { return SKIP; };
        }
        ~Start() override = default;

        const entity::EntityPtr operator()(const entity::EntityPtr entity) override
        {
          return entity::EntityPtr();
        }
      };

      void clearTransforms()
      {
        m_start->stop();
        m_started = false;
        m_start->clear();
        m_start = std::make_shared<Start>();
      }

      bool m_started {false};
      TransformPtr m_start;
      PipelineContextPtr m_context;
      boost::asio::io_context::strand m_strand;
      std::list<Splice> m_splices;
    };
  }  // namespace pipeline
}  // namespace mtconnect
<|endoftext|>