<commit_before>/**
 * projectM -- Milkdrop-esque visualisation SDK
 * Copyright (C)2003-2004 projectM Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * See 'LICENSE.txt' included within this release
 *
 */

#include <stdio.h>

#include "Common.hpp"
#include "fatal.h"
#include "KeyHandler.hpp"
#include "event.h"
#include "BeatDetect.hpp"
#include "PresetChooser.hpp"
#include "Renderer.hpp"
#include "projectM.hpp"

#include <iostream>
#include "TimeKeeper.hpp"

class Preset;
interface_t current_interface = DEFAULT_INTERFACE;

void selectRandom(const bool hardCut);
void selectNext(const bool hardCut);
void selectPrevious(const bool hardCut);

void refreshConsole() {

  switch (current_interface) {

  case MENU_INTERFACE:
    // unimplemented
    break;
  case SHELL_INTERFACE:
    // unimplemented
    break;
  case EDITOR_INTERFACE:
    // unimplemented
    break;
  case DEFAULT_INTERFACE:
    break;
  case BROWSER_INTERFACE:
    // unimplemented
    break;
  default:
    break;
  }

}

void projectM::key_handler( projectMEvent event,
                            projectMKeycode keycode, projectMModifier modifier ) {

	switch( event ) {

	case PROJECTM_KEYDOWN:

	  //default_key_handler();
	  switch (current_interface)
	    {

	    case MENU_INTERFACE:
//	      menu_key_handler(this, event, keycode);
	      break;
	    case SHELL_INTERFACE:
	      //shell_key_handler();
	      break;
	    case EDITOR_INTERFACE:
//	      editor_key_handler(event,keycode);
	      break;
	    case BROWSER_INTERFACE:
//	      browser_key_handler(event,keycode,modifier);
	      break;
	    case DEFAULT_INTERFACE:
	      default_key_handler(event,keycode);
	      break;
	    default:
	      default_key_handler(event,keycode);
	      break;

	    }
	  break;
	default:
		break;

	}
}

void projectM::default_key_handler( projectMEvent event, projectMKeycode keycode) {

	switch( event ) {

	case PROJECTM_KEYDOWN:

	  switch( keycode )
	    {
	    case PROJECTM_K_UP:
            beatDetect->beatSensitivity += 0.25;
			if (beatDetect->beatSensitivity > 5.0) beatDetect->beatSensitivity = 5.0;
	      break;
	    case PROJECTM_K_DOWN:
            beatDetect->beatSensitivity -= 0.25;
			if (beatDetect->beatSensitivity < 0) beatDetect->beatSensitivity = 0;
	      break;
		case PROJECTM_K_h:
 		  renderer->showhelp = !renderer->showhelp;
	      renderer->showstats=false;
	    case PROJECTM_K_F1:
	      renderer->showhelp = !renderer->showhelp;
	      renderer->showstats=false;
	      break;
	    case PROJECTM_K_y:
		this->setShuffleEnabled(!this->isShuffleEnabled());
		 break;

	    case PROJECTM_K_F5:
		  renderer->showfps = !renderer->showfps;
			// Initialize counters and reset frame count.
			renderer->lastTimeFPS = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
			renderer->currentTimeFPS = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
			renderer->totalframes = 0;
			// Hide preset name from screen and replace it with FPS counter.
			if (renderer->showfps)
			{
				renderer->showpreset = false;
			}
	      break;
	    case PROJECTM_K_F4:
		if (!renderer->showhelp)
	       		renderer->showstats = !renderer->showstats;
	       		renderer->showhelp=false;
	      break;
	    case PROJECTM_K_F3: {
	      renderer->showpreset = !renderer->showpreset;
			// Hide FPS from screen and replace it with preset name.
			if (renderer->showpreset)
			{
				renderer->showfps = false;
			}
	      break;
	     }
	    case PROJECTM_K_F2:
	      renderer->showtitle = !renderer->showtitle;
	      break;
#ifndef MACOS
	    case PROJECTM_K_F9:
#else
        case PROJECTM_K_F8:
#endif

	      renderer->studio = !renderer->studio;
	      break;

	    case PROJECTM_K_ESCAPE: {
//	        exit( 1 );
	        break;
	      }
	    case PROJECTM_K_f:

	      break;
	    case PROJECTM_K_a:
		    renderer->correction = !renderer->correction;
	        break;
	    case PROJECTM_K_b:
	      break;
      case PROJECTM_K_H:
      case PROJECTM_K_n:
          selectNext(true);
          break;
      case PROJECTM_K_N:
          selectNext(false);
          break;
	    case PROJECTM_K_r:
		selectRandom(true);
		break;
	    case PROJECTM_K_R:
		selectRandom(false);
		break;
	    case PROJECTM_K_p:
	      selectPrevious(true);
	      break;
	    case PROJECTM_K_P:
	    case PROJECTM_K_BACKSPACE:
	      selectPrevious(false);
	      break;
	    case PROJECTM_K_l:
			setPresetLock(!isPresetLocked());
			break;
	    case PROJECTM_K_s:
            	renderer->studio = !renderer->studio;
	    case PROJECTM_K_i:
	        break;
	    case PROJECTM_K_z:
	      break;
	    case PROJECTM_K_0:
//	      nWaveMode=0;
	      break;
	    case PROJECTM_K_6:
//	      nWaveMode=6;
	      break;
	    case PROJECTM_K_7:
//	      nWaveMode=7;
	      break;
	    case PROJECTM_K_m:
	      break;
	    case PROJECTM_K_t:
	      break;
	    case PROJECTM_K_EQUALS:
	    case PROJECTM_K_PLUS:

	    	unsigned int index;

	    	if (selectedPresetIndex(index)) {

	    		const int oldRating = getPresetRating(index, HARD_CUT_RATING_TYPE);

	    		if (oldRating >= 6)
	    			  break;

	    		const int rating = oldRating + 1;

	    		changePresetRating(index, rating, HARD_CUT_RATING_TYPE);
	    	}

	    	break;

	    case PROJECTM_K_MINUS:
	    	if (selectedPresetIndex(index)) {

	    		const int oldRating = getPresetRating(index, HARD_CUT_RATING_TYPE);

	    		if (oldRating <= 1)
	    			  break;

	    		const int rating = oldRating - 1;

	    		changePresetRating(index, rating, HARD_CUT_RATING_TYPE);
	    	}
	    	break;

	    default:
	      break;
	    }
	default:
		break;

	}
}
<commit_msg>Toast for shuffle enable/disable and beat sensitivity +/-<commit_after>/**
 * projectM -- Milkdrop-esque visualisation SDK
 * Copyright (C)2003-2004 projectM Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * See 'LICENSE.txt' included within this release
 *
 */

#include <stdio.h>

#include "Common.hpp"
#include "fatal.h"
#include "KeyHandler.hpp"
#include "event.h"
#include "BeatDetect.hpp"
#include "PresetChooser.hpp"
#include "Renderer.hpp"
#include "projectM.hpp"

#include <iostream>
#include "TimeKeeper.hpp"

class Preset;
interface_t current_interface = DEFAULT_INTERFACE;

void selectRandom(const bool hardCut);
void selectNext(const bool hardCut);
void selectPrevious(const bool hardCut);

std::string round_float(float number)
{
    std::string num_text = std::to_string(number);
    std::string rounded = num_text.substr(0, num_text.find(".")+3);
	return rounded;
}

void refreshConsole() {

  switch (current_interface) {

  case MENU_INTERFACE:
    // unimplemented
    break;
  case SHELL_INTERFACE:
    // unimplemented
    break;
  case EDITOR_INTERFACE:
    // unimplemented
    break;
  case DEFAULT_INTERFACE:
    break;
  case BROWSER_INTERFACE:
    // unimplemented
    break;
  default:
    break;
  }

}

void projectM::key_handler( projectMEvent event,
                            projectMKeycode keycode, projectMModifier modifier ) {

	switch( event ) {

	case PROJECTM_KEYDOWN:

	  //default_key_handler();
	  switch (current_interface)
	    {

	    case MENU_INTERFACE:
//	      menu_key_handler(this, event, keycode);
	      break;
	    case SHELL_INTERFACE:
	      //shell_key_handler();
	      break;
	    case EDITOR_INTERFACE:
//	      editor_key_handler(event,keycode);
	      break;
	    case BROWSER_INTERFACE:
//	      browser_key_handler(event,keycode,modifier);
	      break;
	    case DEFAULT_INTERFACE:
	      default_key_handler(event,keycode);
	      break;
	    default:
	      default_key_handler(event,keycode);
	      break;

	    }
	  break;
	default:
		break;

	}
}

void projectM::default_key_handler( projectMEvent event, projectMKeycode keycode) {

	switch( event ) {

	case PROJECTM_KEYDOWN:

	  switch( keycode )
	    {
	    case PROJECTM_K_UP:
            beatDetect->beatSensitivity += 0.25;
			if (beatDetect->beatSensitivity > 5.0) beatDetect->beatSensitivity = 5.0;
			renderer->setToastMessage("Beat Sensitivity: "+round_float(beatDetect->beatSensitivity));
	      break;
	    case PROJECTM_K_DOWN:
            beatDetect->beatSensitivity -= 0.25;
			if (beatDetect->beatSensitivity < 0) beatDetect->beatSensitivity = 0;
			renderer->setToastMessage("Beat Sensitivity: "+round_float(beatDetect->beatSensitivity));
	      break;
		case PROJECTM_K_h:
 		  renderer->showhelp = !renderer->showhelp;
	      renderer->showstats=false;
	    case PROJECTM_K_F1:
	      renderer->showhelp = !renderer->showhelp;
	      renderer->showstats=false;
	      break;
	    case PROJECTM_K_y:
		this->setShuffleEnabled(!this->isShuffleEnabled());
		if (this->isShuffleEnabled()) {
			renderer->setToastMessage("Shuffle Enabled");
		}
		else {
			renderer->setToastMessage("Shuffle Disabled");
		}
		 break;

	    case PROJECTM_K_F5:
		  renderer->showfps = !renderer->showfps;
			// Initialize counters and reset frame count.
			renderer->lastTimeFPS = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
			renderer->currentTimeFPS = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
			renderer->totalframes = 0;
			// Hide preset name from screen and replace it with FPS counter.
			if (renderer->showfps)
			{
				renderer->showpreset = false;
			}
	      break;
	    case PROJECTM_K_F4:
		if (!renderer->showhelp)
	       		renderer->showstats = !renderer->showstats;
	       		renderer->showhelp=false;
	      break;
	    case PROJECTM_K_F3: {
	      renderer->showpreset = !renderer->showpreset;
			// Hide FPS from screen and replace it with preset name.
			if (renderer->showpreset)
			{
				renderer->showfps = false;
			}
	      break;
	     }
	    case PROJECTM_K_F2:
	      renderer->showtitle = !renderer->showtitle;
	      break;
#ifndef MACOS
	    case PROJECTM_K_F9:
#else
        case PROJECTM_K_F8:
#endif

	      renderer->studio = !renderer->studio;
	      break;

	    case PROJECTM_K_ESCAPE: {
//	        exit( 1 );
	        break;
	      }
	    case PROJECTM_K_f:

	      break;
	    case PROJECTM_K_a:
		    renderer->correction = !renderer->correction;
	        break;
	    case PROJECTM_K_b:
	      break;
      case PROJECTM_K_H:
      case PROJECTM_K_n:
          selectNext(true);
          break;
      case PROJECTM_K_N:
          selectNext(false);
          break;
	    case PROJECTM_K_r:
		selectRandom(true);
		break;
	    case PROJECTM_K_R:
		selectRandom(false);
		break;
	    case PROJECTM_K_p:
	      selectPrevious(true);
	      break;
	    case PROJECTM_K_P:
	    case PROJECTM_K_BACKSPACE:
	      selectPrevious(false);
	      break;
	    case PROJECTM_K_l:
			setPresetLock(!isPresetLocked());
			break;
	    case PROJECTM_K_s:
            	renderer->studio = !renderer->studio;
	    case PROJECTM_K_i:
	        break;
	    case PROJECTM_K_z:
	      break;
	    case PROJECTM_K_0:
//	      nWaveMode=0;
	      break;
	    case PROJECTM_K_6:
//	      nWaveMode=6;
	      break;
	    case PROJECTM_K_7:
//	      nWaveMode=7;
	      break;
	    case PROJECTM_K_m:
	      break;
	    case PROJECTM_K_t:
	      break;
	    case PROJECTM_K_EQUALS:
	    case PROJECTM_K_PLUS:

	    	unsigned int index;

	    	if (selectedPresetIndex(index)) {

	    		const int oldRating = getPresetRating(index, HARD_CUT_RATING_TYPE);

	    		if (oldRating >= 6)
	    			  break;

	    		const int rating = oldRating + 1;

	    		changePresetRating(index, rating, HARD_CUT_RATING_TYPE);
	    	}

	    	break;

	    case PROJECTM_K_MINUS:
	    	if (selectedPresetIndex(index)) {

	    		const int oldRating = getPresetRating(index, HARD_CUT_RATING_TYPE);

	    		if (oldRating <= 1)
	    			  break;

	    		const int rating = oldRating - 1;

	    		changePresetRating(index, rating, HARD_CUT_RATING_TYPE);
	    	}
	    	break;

	    default:
	      break;
	    }
	default:
		break;

	}
}
<|endoftext|>


<commit_before>#pragma once

#include <types.hpp>
#include <type_traits>

namespace spi {

	struct Configuration
	{
		typedef enum {_2Lines_FullDuplex, _2Lines_RxOnly, _1Line_Rx, _1Line_Tx} Direction_t;
		Direction_t direction;           /*!< Specifies the SPI unidirectional or bidirectional data mode.
											This parameter can be a value of @ref SPI_data_direction */

		typedef enum {Master, Slave} Mode_t;
		Mode_t mode;                /*!< Specifies the SPI operating mode.
											This parameter can be a value of @ref SPI_mode */

		//typedef enum {_8b, _16b} DataSize_t;
		//DataSize_t dataSize;            /*!< Specifies the SPI data size.
		//									This parameter can be a value of @ref SPI_data_size */

		typedef enum {Low, High} ClockPolarity_t;
		ClockPolarity_t clockPolarity;                /*!< Specifies the serial clock steady state.
											This parameter can be a value of @ref SPI_Clock_Polarity */

		typedef enum {_1_Edge, _2_Edge} ClockPhase_t;
		ClockPhase_t clockPhase;                /*!< Specifies the clock active edge for the bit capture.
											This parameter can be a value of @ref SPI_Clock_Phase */

		typedef enum {Soft, Hard} SlaveSelectManagement_t;
		SlaveSelectManagement_t slaveSelectManagement;                 /*!< Specifies whether the NSS signal is managed by
											hardware (NSS pin) or by software using the SSI bit.
											This parameter can be a value of @ref SPI_Slave_Select_management */

		typedef enum {_2, _4, _8, _16, _32, _64, _128, _256} BaudRatePrescaler_t;
		BaudRatePrescaler_t baudRatePrescaler;   /*!< Specifies the Baud Rate prescaler value which will be
											used to configure the transmit and receive SCK clock.
											This parameter can be a value of @ref SPI_BaudRate_Prescaler
											@note The communication clock is derived from the master
											   clock. The slave clock does not need to be set. */

		typedef enum {LSB, MSB} FirstBitTransmission_t;
		FirstBitTransmission_t firstBitTransmission;            /*!< Specifies whether data transfers start from MSB or LSB bit.
											This parameter can be a value of @ref SPI_MSB_LSB_transmission */

		uint16_t CRCPolynomial;       /*!< Specifies the polynomial used for the CRC calculation. */

		static Configuration _default()
		{
			return {_2Lines_FullDuplex, Master, /*_8b,*/ Low, _1_Edge, Soft, _2, MSB, 7};
		}
	};

	class Spi
	{
	public:
		Spi(unsigned int id) : _id(id) {};

		virtual uint8_t send(uint8_t data) = 0;
		virtual uint8_t recv() = 0;

		void (*_callback)(spi::Spi&);
		virtual void register_callback(void (*callback)(spi::Spi&)) = 0;

	protected:
		unsigned int _id;
	};

	unsigned int	num_instance();

	void init_instance(unsigned int id, Configuration config = Configuration::_default());

	Spi& get_instance(unsigned int id);

}
<commit_msg>Add baudrate setting in HAL SPI.<commit_after>#pragma once

#include <types.hpp>
#include <type_traits>

namespace spi {

	struct Configuration
	{
		typedef enum {_2Lines_FullDuplex, _2Lines_RxOnly, _1Line_Rx, _1Line_Tx} Direction_t;
		Direction_t direction;           /*!< Specifies the SPI unidirectional or bidirectional data mode.
											This parameter can be a value of @ref SPI_data_direction */

		typedef enum {Master, Slave} Mode_t;
		Mode_t mode;                /*!< Specifies the SPI operating mode.
											This parameter can be a value of @ref SPI_mode */

		//typedef enum {_8b, _16b} DataSize_t;
		//DataSize_t dataSize;            /*!< Specifies the SPI data size.
		//									This parameter can be a value of @ref SPI_data_size */

		typedef enum {Low, High} ClockPolarity_t;
		ClockPolarity_t clockPolarity;                /*!< Specifies the serial clock steady state.
											This parameter can be a value of @ref SPI_Clock_Polarity */

		typedef enum {_1_Edge, _2_Edge} ClockPhase_t;
		ClockPhase_t clockPhase;                /*!< Specifies the clock active edge for the bit capture.
											This parameter can be a value of @ref SPI_Clock_Phase */

		typedef enum {Soft, Hard} SlaveSelectManagement_t;
		SlaveSelectManagement_t slaveSelectManagement;                 /*!< Specifies whether the NSS signal is managed by
											hardware (NSS pin) or by software using the SSI bit.
											This parameter can be a value of @ref SPI_Slave_Select_management */

		typedef enum {_2, _4, _8, _16, _32, _64, _128, _256} BaudRatePrescaler_t;
		BaudRatePrescaler_t baudRatePrescaler;   /*!< Specifies the Baud Rate prescaler value which will be
											used to configure the transmit and receive SCK clock.
											This parameter can be a value of @ref SPI_BaudRate_Prescaler
											@note The communication clock is derived from the master
											   clock. The slave clock does not need to be set. */

		typedef enum {LSB, MSB} FirstBitTransmission_t;
		FirstBitTransmission_t firstBitTransmission;            /*!< Specifies whether data transfers start from MSB or LSB bit.
											This parameter can be a value of @ref SPI_MSB_LSB_transmission */

		unsigned long CRCPolynomial;       /*!< Specifies the polynomial used for the CRC calculation. */

		unsigned long DataRate;       /*!< Specifies the data rate. */

		static Configuration _default()
		{
			return {_2Lines_FullDuplex, Master, /*_8b,*/ Low, _1_Edge, Soft, _2, MSB, 8, 1000000};
		}
	};

	class Spi
	{
	public:
		Spi(unsigned int id) : _id(id) {};

		virtual uint8_t send(uint8_t data) = 0;
		virtual uint8_t recv() = 0;

		void (*_callback)(spi::Spi&);
		virtual void register_callback(void (*callback)(spi::Spi&)) = 0;

	protected:
		unsigned int _id;
	};

	unsigned int	num_instance();

	void init_instance(unsigned int id, Configuration config = Configuration::_default());

	Spi& get_instance(unsigned int id);

}
<|endoftext|>


<commit_before>/**
*  Copyright (C) 2015 3D Repo Ltd
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU Affero General Public License as
*  published by the Free Software Foundation, either version 3 of the
*  License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Affero General Public License for more details.
*
*  You should have received a copy of the GNU Affero General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "functions.h"

#include <sstream>

static const std::string cmdCleanProj = "clean"; //clean up a specified project
static const std::string cmdGenStash = "genStash";   //test the connection
static const std::string cmdGetFile = "getFile"; //download original file
static const std::string cmdImportFile = "import"; //file import
static const std::string cmdTestConn = "test";   //test the connection
static const std::string cmdVersion = "version";   //get version
static const std::string cmdVersion2 = "-v";   //get version

std::string helpInfo()
{
	std::stringstream ss;

	ss << cmdGenStash << "\tGenerate Stash for a project. (args: database project [repo|gltf|src|tree])\n";
	ss << cmdGetFile << "\t\tGet original file for the latest revision of the project (args: database project dir)\n";
	ss << cmdImportFile << "\t\tImport file to database. (args: file database project [dxrotate] [owner] [configfile])\n";
	ss << cmdCleanProj << "\t\tClean up a specified project removing/repairing corrupted revisions. (args: database project)\n";
	ss << cmdTestConn << "\t\tTest the client and database connection is working. (args: none)\n";
	ss << cmdVersion << "[-v]\tPrints the version of Repo Bouncer Client/Library\n";

	return ss.str();
}

bool isSpecialCommand(const std::string &cmd)
{
	return cmd == cmdVersion || cmd == cmdVersion2;
}

int32_t knownValid(const std::string &cmd)
{
	if (cmd == cmdImportFile)
		return 3;
	if (cmd == cmdGenStash)
		return 3;
	if (cmd == cmdCleanProj)
		return 2;
	if (cmd == cmdGetFile)
		return 3;
	if (cmd == cmdTestConn)
		return 0;
	if (cmd == cmdVersion || cmd == cmdVersion2)
		return 0;
	return -1;
}

int32_t performOperation(

	repo::RepoController *controller,
	const repo::RepoController::RepoToken      *token,
	const repo_op_t            &command
	)
{
	int32_t errCode = REPOERR_UNKNOWN_CMD;

	if (command.command == cmdImportFile)
	{
		try{
			errCode = importFileAndCommit(controller, token, command);
		}
		catch (const std::exception &e)
		{
			repoLogError("Failed to import and commit file: " + std::string(e.what()));
			errCode = REPOERR_UNKNOWN_ERR;
		}
	}
	else if (command.command == cmdGenStash)
	{
		try{
			errCode = generateStash(controller, token, command);
		}
		catch (const std::exception &e)
		{
			repoLogError("Failed to generate optimised stash: " + std::string(e.what()));
			errCode = REPOERR_UNKNOWN_ERR;
		}
	}
	else if (command.command == cmdCleanProj)
	{
		try{
			errCode = cleanUpProject(controller, token, command);
		}
		catch (const std::exception &e)
		{
			repoLogError("Failed to generate optimised stash: " + std::string(e.what()));
			errCode = REPOERR_UNKNOWN_ERR;
		}
	}
	else if (command.command == cmdGetFile)
	{
		try{
			errCode = getFileFromProject(controller, token, command);
		}
		catch (const std::exception &e)
		{
			repoLogError("Failed to retrieve file from project: " + std::string(e.what()));
			errCode = REPOERR_UNKNOWN_ERR;
		}
	}
	else if (command.command == cmdTestConn)
	{
		//This is just to test if the client is working and if the connection is working
		//if we got a token from the controller we can assume that it worked.
		return token ? REPOERR_OK : REPOERR_AUTH_FAILED;
	}
	else if (command.command == cmdVersion || command.command == cmdVersion2)
	{
		std::cout << "3D Repo Bouncer Client v" + controller->getVersion() << std::endl;
		errCode = REPOERR_OK;
	}
	else
		repoLogError("Unrecognised command: " + command.command + ". Type --help for info");

	return errCode;
}

/*
* ======================== Command functions ===================
*/

int32_t cleanUpProject(
	repo::RepoController       *controller,
	const repo::RepoController::RepoToken      *token,
	const repo_op_t            &command
	)
{
	/*
	* Check the amount of parameters matches
	*/
	if (command.nArgcs < 2)
	{
		repoLogError("Number of arguments mismatch! " + cmdCleanProj
			+ " requires 2 arguments:database project");
		return REPOERR_INVALID_ARG;
	}

	std::string dbName = command.args[0];
	std::string project = command.args[1];

	controller->cleanUp(token, dbName, project);

	return REPOERR_OK;
}

int32_t generateStash(
	repo::RepoController       *controller,
	const repo::RepoController::RepoToken      *token,
	const repo_op_t            &command
	)
{
	/*
	* Check the amount of parameters matches
	*/
	if (command.nArgcs < 3)
	{
		repoLogError("Number of arguments mismatch! " + cmdGenStash
			+ " requires 3 arguments:database project [repo|gltf|src|tree]");
		return REPOERR_INVALID_ARG;
	}

	std::string dbName = command.args[0];
	std::string project = command.args[1];
	std::string type = command.args[2];

	if (!(type == "repo" || type == "gltf" || type == "src" || type == "tree"))
	{
		repoLogError("Unknown stash type: " + type);
		return REPOERR_INVALID_ARG;
	}

	auto scene = controller->fetchScene(token, dbName, project);
	if (!scene)
	{
		return REPOERR_LOAD_SCENE_FAIL;
	}

	bool  success = false;

	if (type == "repo")
	{
		success = controller->generateAndCommitStashGraph(token, scene);
	}
	else if (type == "gltf")
	{
		success = controller->generateAndCommitGLTFBuffer(token, scene);
	}
	else if (type == "src")
	{
		success = controller->generateAndCommitSRCBuffer(token, scene);
	}
	else if (type == "tree")
	{
		success = controller->generateAndCommitSelectionTree(token, scene);
	}

	return success ? REPOERR_OK : REPOERR_STASH_GEN_FAIL;
}

int32_t getFileFromProject(
	repo::RepoController       *controller,
	const repo::RepoController::RepoToken      *token,
	const repo_op_t            &command
	)
{
	/*
	* Check the amount of parameters matches
	*/
	if (command.nArgcs < 3)
	{
		repoLogError("Number of arguments mismatch! " + cmdGenStash
			+ " requires 3 arguments:database project dir");
		return REPOERR_INVALID_ARG;
	}

	std::string dbName = command.args[0];
	std::string project = command.args[1];
	std::string dir = command.args[2];

	controller->saveOriginalFiles(token, dbName, project, dir);

	return REPOERR_OK;
}

int32_t importFileAndCommit(
	repo::RepoController *controller,
	const repo::RepoController::RepoToken      *token,
	const repo_op_t            &command
	)
{
	/*
	* Check the amount of parameters matches
	*/
	if (command.nArgcs < 3)
	{
		repoLogError("Number of arguments mismatch! " + cmdImportFile
			+ " requires 3 arguments: file database project [dxrotate] [owner] [config file]");
		return REPOERR_INVALID_ARG;
	}

	std::string fileLoc = command.args[0];
	std::string database = command.args[1];
	std::string project = command.args[2];
	std::string configFile;
	std::string owner;
	bool rotate = false;

	//FIXME: This is getting complicated, we should consider using boost::program_options and start utilising flags...
	//Something like this: http://stackoverflow.com/questions/15541498/how-to-implement-subcommands-using-boost-program-options
	if (command.nArgcs > 3)
	{
		//If 3rd argument is "dxrotate", we need to rotate the X axis
		//Otherwise the user is trying to name the owner, rotate is false.
		std::string arg3 = command.args[3];
		if (arg3 == "dxrotate")
		{
			rotate = true;
		}
		else
		{
			owner = command.args[3];
		}
	}
	if (command.nArgcs > 4)
	{
		//If the last argument is rotate, this is owner
		//otherwise this is configFile (confusing, I know.)
		if (rotate)
		{
			owner = command.args[4];
			if (command.nArgcs > 5)
			{
				configFile = command.args[5];
			}
		}
		else
		{
			configFile = command.args[4];
		}
	}

	repoLogDebug("File: " + fileLoc + " database: " + database
		+ " project: " + project + " rotate:"
		+ (rotate ? "true" : "false") + " owner :" + owner + " configFile: " + configFile);

	repo::manipulator::modelconvertor::ModelImportConfig config(configFile);

	repo::core::model::RepoScene *graph = controller->loadSceneFromFile(fileLoc, &config);
	if (graph)
	{
		repoLog("Trying to commit this scene to database as " + database + "." + project);
		graph->setDatabaseAndProjectName(database, project);
		if (rotate)
			graph->reorientateDirectXModel();
		if (owner.empty())
			controller->commitScene(token, graph);
		else
			controller->commitScene(token, graph, owner);
		//FIXME: should make commitscene return a boolean even though GUI doesn't care...
		if (graph->isMissingTexture())
		{
			repoLog("Missing texture detected!");
			return REPOERR_LOAD_SCENE_MISSING_TEXTURE;
		}
		else
			return REPOERR_OK;
	}

	return REPOERR_LOAD_SCENE_FAIL;
}<commit_msg>#13 rotate scene as it loads<commit_after>/**
*  Copyright (C) 2015 3D Repo Ltd
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU Affero General Public License as
*  published by the Free Software Foundation, either version 3 of the
*  License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Affero General Public License for more details.
*
*  You should have received a copy of the GNU Affero General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "functions.h"

#include <sstream>

static const std::string cmdCleanProj = "clean"; //clean up a specified project
static const std::string cmdGenStash = "genStash";   //test the connection
static const std::string cmdGetFile = "getFile"; //download original file
static const std::string cmdImportFile = "import"; //file import
static const std::string cmdTestConn = "test";   //test the connection
static const std::string cmdVersion = "version";   //get version
static const std::string cmdVersion2 = "-v";   //get version

std::string helpInfo()
{
	std::stringstream ss;

	ss << cmdGenStash << "\tGenerate Stash for a project. (args: database project [repo|gltf|src|tree])\n";
	ss << cmdGetFile << "\t\tGet original file for the latest revision of the project (args: database project dir)\n";
	ss << cmdImportFile << "\t\tImport file to database. (args: file database project [dxrotate] [owner] [configfile])\n";
	ss << cmdCleanProj << "\t\tClean up a specified project removing/repairing corrupted revisions. (args: database project)\n";
	ss << cmdTestConn << "\t\tTest the client and database connection is working. (args: none)\n";
	ss << cmdVersion << "[-v]\tPrints the version of Repo Bouncer Client/Library\n";

	return ss.str();
}

bool isSpecialCommand(const std::string &cmd)
{
	return cmd == cmdVersion || cmd == cmdVersion2;
}

int32_t knownValid(const std::string &cmd)
{
	if (cmd == cmdImportFile)
		return 3;
	if (cmd == cmdGenStash)
		return 3;
	if (cmd == cmdCleanProj)
		return 2;
	if (cmd == cmdGetFile)
		return 3;
	if (cmd == cmdTestConn)
		return 0;
	if (cmd == cmdVersion || cmd == cmdVersion2)
		return 0;
	return -1;
}

int32_t performOperation(

	repo::RepoController *controller,
	const repo::RepoController::RepoToken      *token,
	const repo_op_t            &command
	)
{
	int32_t errCode = REPOERR_UNKNOWN_CMD;

	if (command.command == cmdImportFile)
	{
		try{
			errCode = importFileAndCommit(controller, token, command);
		}
		catch (const std::exception &e)
		{
			repoLogError("Failed to import and commit file: " + std::string(e.what()));
			errCode = REPOERR_UNKNOWN_ERR;
		}
	}
	else if (command.command == cmdGenStash)
	{
		try{
			errCode = generateStash(controller, token, command);
		}
		catch (const std::exception &e)
		{
			repoLogError("Failed to generate optimised stash: " + std::string(e.what()));
			errCode = REPOERR_UNKNOWN_ERR;
		}
	}
	else if (command.command == cmdCleanProj)
	{
		try{
			errCode = cleanUpProject(controller, token, command);
		}
		catch (const std::exception &e)
		{
			repoLogError("Failed to generate optimised stash: " + std::string(e.what()));
			errCode = REPOERR_UNKNOWN_ERR;
		}
	}
	else if (command.command == cmdGetFile)
	{
		try{
			errCode = getFileFromProject(controller, token, command);
		}
		catch (const std::exception &e)
		{
			repoLogError("Failed to retrieve file from project: " + std::string(e.what()));
			errCode = REPOERR_UNKNOWN_ERR;
		}
	}
	else if (command.command == cmdTestConn)
	{
		//This is just to test if the client is working and if the connection is working
		//if we got a token from the controller we can assume that it worked.
		return token ? REPOERR_OK : REPOERR_AUTH_FAILED;
	}
	else if (command.command == cmdVersion || command.command == cmdVersion2)
	{
		std::cout << "3D Repo Bouncer Client v" + controller->getVersion() << std::endl;
		errCode = REPOERR_OK;
	}
	else
		repoLogError("Unrecognised command: " + command.command + ". Type --help for info");

	return errCode;
}

/*
* ======================== Command functions ===================
*/

int32_t cleanUpProject(
	repo::RepoController       *controller,
	const repo::RepoController::RepoToken      *token,
	const repo_op_t            &command
	)
{
	/*
	* Check the amount of parameters matches
	*/
	if (command.nArgcs < 2)
	{
		repoLogError("Number of arguments mismatch! " + cmdCleanProj
			+ " requires 2 arguments:database project");
		return REPOERR_INVALID_ARG;
	}

	std::string dbName = command.args[0];
	std::string project = command.args[1];

	controller->cleanUp(token, dbName, project);

	return REPOERR_OK;
}

int32_t generateStash(
	repo::RepoController       *controller,
	const repo::RepoController::RepoToken      *token,
	const repo_op_t            &command
	)
{
	/*
	* Check the amount of parameters matches
	*/
	if (command.nArgcs < 3)
	{
		repoLogError("Number of arguments mismatch! " + cmdGenStash
			+ " requires 3 arguments:database project [repo|gltf|src|tree]");
		return REPOERR_INVALID_ARG;
	}

	std::string dbName = command.args[0];
	std::string project = command.args[1];
	std::string type = command.args[2];

	if (!(type == "repo" || type == "gltf" || type == "src" || type == "tree"))
	{
		repoLogError("Unknown stash type: " + type);
		return REPOERR_INVALID_ARG;
	}

	auto scene = controller->fetchScene(token, dbName, project);
	if (!scene)
	{
		return REPOERR_LOAD_SCENE_FAIL;
	}

	bool  success = false;

	if (type == "repo")
	{
		success = controller->generateAndCommitStashGraph(token, scene);
	}
	else if (type == "gltf")
	{
		success = controller->generateAndCommitGLTFBuffer(token, scene);
	}
	else if (type == "src")
	{
		success = controller->generateAndCommitSRCBuffer(token, scene);
	}
	else if (type == "tree")
	{
		success = controller->generateAndCommitSelectionTree(token, scene);
	}

	return success ? REPOERR_OK : REPOERR_STASH_GEN_FAIL;
}

int32_t getFileFromProject(
	repo::RepoController       *controller,
	const repo::RepoController::RepoToken      *token,
	const repo_op_t            &command
	)
{
	/*
	* Check the amount of parameters matches
	*/
	if (command.nArgcs < 3)
	{
		repoLogError("Number of arguments mismatch! " + cmdGenStash
			+ " requires 3 arguments:database project dir");
		return REPOERR_INVALID_ARG;
	}

	std::string dbName = command.args[0];
	std::string project = command.args[1];
	std::string dir = command.args[2];

	controller->saveOriginalFiles(token, dbName, project, dir);

	return REPOERR_OK;
}

int32_t importFileAndCommit(
	repo::RepoController *controller,
	const repo::RepoController::RepoToken      *token,
	const repo_op_t            &command
	)
{
	/*
	* Check the amount of parameters matches
	*/
	if (command.nArgcs < 3)
	{
		repoLogError("Number of arguments mismatch! " + cmdImportFile
			+ " requires 3 arguments: file database project [dxrotate] [owner] [config file]");
		return REPOERR_INVALID_ARG;
	}

	std::string fileLoc = command.args[0];
	std::string database = command.args[1];
	std::string project = command.args[2];
	std::string configFile;
	std::string owner;
	bool rotate = false;

	//FIXME: This is getting complicated, we should consider using boost::program_options and start utilising flags...
	//Something like this: http://stackoverflow.com/questions/15541498/how-to-implement-subcommands-using-boost-program-options
	if (command.nArgcs > 3)
	{
		//If 3rd argument is "dxrotate", we need to rotate the X axis
		//Otherwise the user is trying to name the owner, rotate is false.
		std::string arg3 = command.args[3];
		if (arg3 == "dxrotate")
		{
			rotate = true;
		}
		else
		{
			owner = command.args[3];
		}
	}
	if (command.nArgcs > 4)
	{
		//If the last argument is rotate, this is owner
		//otherwise this is configFile (confusing, I know.)
		if (rotate)
		{
			owner = command.args[4];
			if (command.nArgcs > 5)
			{
				configFile = command.args[5];
			}
		}
		else
		{
			configFile = command.args[4];
		}
	}

	repoLogDebug("File: " + fileLoc + " database: " + database
		+ " project: " + project + " rotate:"
		+ (rotate ? "true" : "false") + " owner :" + owner + " configFile: " + configFile);

	repo::manipulator::modelconvertor::ModelImportConfig config(configFile);

	repo::core::model::RepoScene *graph = controller->loadSceneFromFile(fileLoc, true, rotate, &config);
	if (graph)
	{
		repoLog("Trying to commit this scene to database as " + database + "." + project);
		graph->setDatabaseAndProjectName(database, project);

		if (owner.empty())
			controller->commitScene(token, graph);
		else
			controller->commitScene(token, graph, owner);
		//FIXME: should make commitscene return a boolean even though GUI doesn't care...
		if (graph->isMissingTexture())
		{
			repoLog("Missing texture detected!");
			return REPOERR_LOAD_SCENE_MISSING_TEXTURE;
		}
		else
			return REPOERR_OK;
	}

	return REPOERR_LOAD_SCENE_FAIL;
}<|endoftext|>


<commit_before>/**
\file ll_translation_control.hpp
\brief Defines class LLTranslationControl and defines its methods.
\author Radek Vít
*/
#ifndef CTF_LL_TRANSLATION_CONTROL_H
#define CTF_LL_TRANSLATION_CONTROL_H

#include "translation_control.hpp"

namespace ctf {

/**
\brief Implements LL top down translation control.
*/
class LLTranslationControl : public TranslationControl {
 protected:
  /**
  \brief Empty set for each nonterminal.
  */
  vector<bool> empty_;
  /**
  \brief First set for each nonterminal.
  */
  vector<vector<Symbol>> first_;
  /**
  \brief Follow set for each nonterminal.
  */
  vector<vector<Symbol>> follow_;
  /**
  \brief Predict set for each nonterminal.
  */
  vector<vector<Symbol>> predict_;

  /**
  \brief LL table used to control the translation.
  */
  LLTable llTable_;

  /**
  \brief Error message string.
  */
  string errorString_;

  /**
  Creates all predictive sets and creates a new LL table.
  */
  void create_ll_table() {
    create_empty();
    create_first();
    create_follow();
    create_predict();

    llTable_ = LLTable(*translationGrammar_, predict_);
  }

  /**
  \brief Creates Empty set for each nonterminal.

  Empty is true if a series of productions from the nonterminal can result in an
  empty string.
  */
  void create_empty() {
    const TranslationGrammar &tg = *translationGrammar_;
    empty_ = vector<bool>(tg.nonterminals().size(), false);

    for (auto &r : tg.rules()) {
      if (r.input().size() == 0) {
        empty_[tg.nonterminal_index(r.nonterminal())] = true;
      }
    }

    bool changed = false;
    do {
      changed = false;
      for (auto &r : tg.rules()) {
        bool isempty = true;
        for (auto &s : r.input()) {
          switch (s.type()) {
            case Symbol::Type::TERMINAL:
              isempty = false;
              break;
            case Symbol::Type::NONTERMINAL:
              if (empty_[tg.nonterminal_index(s)] == false) {
                isempty = false;
              }
              break;
            default:
              break;
          }
        }
        if (isempty) {
          if (!empty_[tg.nonterminal_index(r.nonterminal())]) {
            changed = true;
            empty_[tg.nonterminal_index(r.nonterminal())] = true;
          }
        }
      }
    } while (changed);
  }
  /**
  \brief Creates First set for each nonterminal.

  First contains all characters that can be at the first position of any string
  derived from this nonterminal.
  */
  void create_first() {
    const TranslationGrammar &tg = *translationGrammar_;
    first_ = {tg.nonterminals().size(), vector<Symbol>{}};

    bool changed = false;
    do {
      changed = false;
      for (auto &r : tg.rules()) {
        size_t i = tg.nonterminal_index(r.nonterminal());
        bool empty = true;
        for (auto &symbol : r.input()) {
          if (!empty)
            break;
          size_t nonterm_i;
          switch (symbol.type()) {
            case Symbol::Type::NONTERMINAL:
              nonterm_i = tg.nonterminal_index(symbol);
              if (modify_set(first_[i], first_[nonterm_i]))
                changed = true;
              empty = empty_[nonterm_i];
              break;
            case Symbol::Type::TERMINAL:
              if (modify_set(first_[i], vector<Symbol>({symbol})))
                changed = true;
              empty = false;
              break;
            default:
              break;
          }
        }
      }
    } while (changed);
  }
  /**
  \brief Creates Follow set for each nonterminal.

  Follow contains all characters that may follow that nonterminal in a
  sentential form from the starting nonterminal.
  */
  void create_follow() {
    const TranslationGrammar &tg = *translationGrammar_;
    follow_ = {tg.nonterminals().size(), vector<Symbol>{}};
    follow_[tg.nonterminal_index(tg.starting_symbol())].push_back(
        Symbol::eof());

    bool changed = false;
    do {
      changed = false;
      for (auto &r : tg.rules()) {
        // index of origin nonterminal
        size_t i = tg.nonterminal_index(r.nonterminal());
        /* empty set of all symbols to the right of the current one */
        bool compoundEmpty = true;
        /* first set of all symbols to the right of the current symbol */
        vector<Symbol> compoundFirst;
        /* track symbols from back */
        for (auto &s : reverse(r.input())) {
          // index of nonterminal in input string, only valid with
          // nonterminal symbol
          size_t ti = 0;
          switch (s.type()) {
            case Symbol::Type::NONTERMINAL:
              ti = tg.nonterminal_index(s);
              if (modify_set(follow_[ti], compoundFirst))
                changed = true;
              if (compoundEmpty && modify_set(follow_[ti], follow_[i]))
                changed = true;
              break;
            default:
              break;
          }
          /* if empty = false */
          if (s.type() != Symbol::Type::NONTERMINAL ||
              !empty_[tg.nonterminal_index(s)]) {
            compoundEmpty = false;
            switch (s.type()) {
              case Symbol::Type::NONTERMINAL:
                compoundFirst = first_[ti];
                break;
              case Symbol::Type::TERMINAL:
                compoundFirst = {s};
                break;
              default:
                break;
            }
          }
          /* empty = true, nonterminal*/
          else {
            modify_set(compoundFirst, first_[ti]);
          }
        }  // for all reverse input
      }    // for all rules
    } while (changed);
  }
  /**
  \brief Creates Predict set for each nonterminal.

  Predict contains all Terminals that may be the first terminal read in a
  sentential form from that nonterminal.
  */
  void create_predict() {
    predict_.clear();
    const TranslationGrammar &tg = *translationGrammar_;
    for (auto &r : tg.rules()) {
      vector<Symbol> compoundFirst;
      vector<Symbol> rfollow = follow_[tg.nonterminal_index(r.nonterminal())];
      bool compoundEmpty = true;
      for (auto &s : reverse(r.input())) {
        size_t i;
        switch (s.type()) {
          case Symbol::Type::TERMINAL:
            compoundEmpty = false;
            compoundFirst = vector<Symbol>({s});
            break;
          case Symbol::Type::NONTERMINAL:
            i = tg.nonterminal_index(s);
            if (!empty_[i]) {
              compoundEmpty = false;
              compoundFirst = first_[i];
            } else {
              modify_set(compoundFirst, first_[i]);
            }
          default:
            break;
        }
      }
      predict_.push_back(compoundFirst);

      if (compoundEmpty) {
        modify_set(predict_.back(), rfollow);
      }
    }  // for all rules
  }

  /**
  \brief Creates iterator attribute actions for incoming terminals.

  \param[in] obegin Iterator to the first Symbol of the output of the applied
  Rule.
  \param[in] targets Indices of the target actions for all input terminals.
  \param[out] attributeActions Targets to append incoming terminal's attributes.

  The added iterators point to input terminal attribute targets.
  */
  void create_attibute_actions(
      tstack<Symbol>::iterator obegin, const vector<set<size_t>> &targets,
      tstack<vector<tstack<Symbol>::iterator>> &attributeActions) {
    for (auto &target : reverse(targets)) {
      vector<tstack<Symbol>::iterator> iterators;
      for (auto &i : target) {
        auto oit = obegin;
        for (size_t x = 0; x < i; ++x)
          ++oit;
        if (oit->type() == Symbol::Type::TERMINAL)
          iterators.push_back(oit);
      }
      attributeActions.push(iterators);
    }
  }

 public:
  /**
  \brief Constructs a LLTranslationControl.
  */
  LLTranslationControl() = default;
  /**
  \brief Default destructor.
  */
  virtual ~LLTranslationControl() = default;
  /**
  \brief Constructs LLTranslationControl with a LexicalAnalyzer and
  TranslationGrammar.

  \param[in] la A reference to the lexical analyzer to be used to get tokens.
  \param[in] tg The translation grammar for this translation.
  */
  LLTranslationControl(LexicalAnalyzer &la, TranslationGrammar &tg) {
    set_grammar(tg);
    set_lexical_analyzer(la);
  }

  /**
  \brief Sets translation grammar.

  \param[in] tg The translation grammar for this translation.
  */
  virtual void set_grammar(const TranslationGrammar &tg) {
    translationGrammar_ = &tg;
    create_ll_table();
  }

  /**
  \brief Runs the translation. Output symbols are stored in output_.
  */
  virtual void run() {
    using Type = Symbol::Type;

    if (!lexicalAnalyzer_)
      throw TranslationException("No lexical analyzer was attached.");
    else if (!translationGrammar_)
      throw TranslationException("No translation grammar was attached.");

    input_.clear();
    output_.clear();
    tstack<vector<tstack<Symbol>::iterator>> attributeActions;

    Symbol token = next_token();

    input_.push(Symbol::eof());
    output_.push(Symbol::eof());
    input_.push(translationGrammar_->starting_symbol());
    output_.push(translationGrammar_->starting_symbol());

    // iterator to the first symbol of the last inserted string
    // used to speed up output_ linear search
    auto obegin = output_.begin();

    while (1) {
      Symbol &top = input_.top();
      size_t ruleIndex;
      switch (top.type()) {
        case Type::EOI:
          if (token == Symbol::eof()) {
            return;
          } else {
            add_error(top, token);
            return;
          }
          break;
        case Type::TERMINAL:
          if (top == token) {
            for (auto it : attributeActions.pop()) {
              it->set_attribute(token);
            }
            input_.pop();
            token = next_token();
          } else {
            add_error(top, token);
            // TODO error recovery
            return;
          }
          break;
        case Type::NONTERMINAL:
          ruleIndex = llTable_.rule_index(top, token);
          if (ruleIndex < translationGrammar_->rules().size()) {
            auto &rule = translationGrammar_->rules()[ruleIndex];

            obegin = output_.replace(top, rule.output(), obegin);
            input_.replace(input_.begin(), rule.input());
            create_attibute_actions(obegin, rule.actions(), attributeActions);
          } else {
            add_error(top, token);
            // TODO error recovery
            return;
          }
          break;
        default:
          // unexpected symbol type on input stack
          input_.pop();
          break;
      }
    }
  }

  /**
  \brief Adds error message caused by a top symbol and incoming token
  combination.

  \param[in] top The current top symbol.
  \param[in] token The incoming token.
  */
  virtual void add_error(const Symbol &top, const Symbol &token) {
    using Type = Symbol::Type;

    errorFlag_ = true;
    errorString_ += token.location().to_string() + ": ";
    switch (top.type()) {
      case Type::EOI:
        errorString_ += "Unexpected token '" + token.name() +
                        "' after translation has finished.";
        break;
      case Type::TERMINAL:
        errorString_ += "Unexpected token '" + token.name() + "'; expected '" +
                        top.name() + "'";
        break;
      case Type::NONTERMINAL:
        // TODO list expected tokens
        errorString_ += "Unexpected token '" + token.name() +
                        "', nonterminal '" + top.name() + "'";
        break;
      default:
        break;
    }
    errorString_ += "\n";
  }

  // TODO error recovery:
  // skip to next input token in follow of last encountered nonterminal.

  /**
  \brief Get error message.

  \returns The error message string.
  */
  string error_message() { return errorString_; }
};
}  // namespace ctf
#endif
/*** End of file ll_translation_control.hpp ***/<commit_msg>implemented Hartmann error recovery<commit_after>/**
\file ll_translation_control.hpp
\brief Defines class LLTranslationControl and defines its methods.
\author Radek Vít
*/
#ifndef CTF_LL_TRANSLATION_CONTROL_H
#define CTF_LL_TRANSLATION_CONTROL_H

#include "translation_control.hpp"

namespace ctf {

/**
\brief Implements LL top down translation control.
*/
class LLTranslationControl : public TranslationControl {
 protected:
  /**
  \brief Empty set for each nonterminal.
  */
  vector<bool> empty_;
  /**
  \brief First set for each nonterminal.
  */
  vector<vector<Symbol>> first_;
  /**
  \brief Follow set for each nonterminal.
  */
  vector<vector<Symbol>> follow_;
  /**
  \brief Predict set for each nonterminal.
  */
  vector<vector<Symbol>> predict_;

  /**
  \brief LL table used to control the translation.
  */
  LLTable llTable_;

  /**
  \brief Error message string.
  */
  string errorString_;

  /**
  \brief The last expanded nonterminal.
  */
  Symbol lastNonterminal_ = Symbol::eof();

  /**
  Creates all predictive sets and creates a new LL table.
  */
  void create_ll_table() {
    create_empty();
    create_first();
    create_follow();
    create_predict();

    llTable_ = LLTable(*translationGrammar_, predict_);
  }

  /**
  \brief Creates Empty set for each nonterminal.

  Empty is true if a series of productions from the nonterminal can result in an
  empty string.
  */
  void create_empty() {
    const TranslationGrammar &tg = *translationGrammar_;
    empty_ = vector<bool>(tg.nonterminals().size(), false);

    for (auto &r : tg.rules()) {
      if (r.input().size() == 0) {
        empty_[tg.nonterminal_index(r.nonterminal())] = true;
      }
    }

    bool changed = false;
    do {
      changed = false;
      for (auto &r : tg.rules()) {
        bool isempty = true;
        for (auto &s : r.input()) {
          switch (s.type()) {
            case Symbol::Type::TERMINAL:
              isempty = false;
              break;
            case Symbol::Type::NONTERMINAL:
              if (empty_[tg.nonterminal_index(s)] == false) {
                isempty = false;
              }
              break;
            default:
              break;
          }
        }
        if (isempty) {
          if (!empty_[tg.nonterminal_index(r.nonterminal())]) {
            changed = true;
            empty_[tg.nonterminal_index(r.nonterminal())] = true;
          }
        }
      }
    } while (changed);
  }
  /**
  \brief Creates First set for each nonterminal.

  First contains all characters that can be at the first position of any string
  derived from this nonterminal.
  */
  void create_first() {
    const TranslationGrammar &tg = *translationGrammar_;
    first_ = {tg.nonterminals().size(), vector<Symbol>{}};

    bool changed = false;
    do {
      changed = false;
      for (auto &r : tg.rules()) {
        size_t i = tg.nonterminal_index(r.nonterminal());
        bool empty = true;
        for (auto &symbol : r.input()) {
          if (!empty)
            break;
          size_t nonterm_i;
          switch (symbol.type()) {
            case Symbol::Type::NONTERMINAL:
              nonterm_i = tg.nonterminal_index(symbol);
              if (modify_set(first_[i], first_[nonterm_i]))
                changed = true;
              empty = empty_[nonterm_i];
              break;
            case Symbol::Type::TERMINAL:
              if (modify_set(first_[i], vector<Symbol>({symbol})))
                changed = true;
              empty = false;
              break;
            default:
              break;
          }
        }
      }
    } while (changed);
  }
  /**
  \brief Creates Follow set for each nonterminal.

  Follow contains all characters that may follow that nonterminal in a
  sentential form from the starting nonterminal.
  */
  void create_follow() {
    const TranslationGrammar &tg = *translationGrammar_;
    follow_ = {tg.nonterminals().size(), vector<Symbol>{}};
    follow_[tg.nonterminal_index(tg.starting_symbol())].push_back(
        Symbol::eof());

    bool changed = false;
    do {
      changed = false;
      for (auto &r : tg.rules()) {
        // index of origin nonterminal
        size_t i = tg.nonterminal_index(r.nonterminal());
        /* empty set of all symbols to the right of the current one */
        bool compoundEmpty = true;
        /* first set of all symbols to the right of the current symbol */
        vector<Symbol> compoundFirst;
        /* track symbols from back */
        for (auto &s : reverse(r.input())) {
          // index of nonterminal in input string, only valid with
          // nonterminal symbol
          size_t ti = 0;
          switch (s.type()) {
            case Symbol::Type::NONTERMINAL:
              ti = tg.nonterminal_index(s);
              if (modify_set(follow_[ti], compoundFirst))
                changed = true;
              if (compoundEmpty && modify_set(follow_[ti], follow_[i]))
                changed = true;
              break;
            default:
              break;
          }
          /* if empty = false */
          if (s.type() != Symbol::Type::NONTERMINAL ||
              !empty_[tg.nonterminal_index(s)]) {
            compoundEmpty = false;
            switch (s.type()) {
              case Symbol::Type::NONTERMINAL:
                compoundFirst = first_[ti];
                break;
              case Symbol::Type::TERMINAL:
                compoundFirst = {s};
                break;
              default:
                break;
            }
          }
          /* empty = true, nonterminal*/
          else {
            modify_set(compoundFirst, first_[ti]);
          }
        }  // for all reverse input
      }    // for all rules
    } while (changed);
  }
  /**
  \brief Creates Predict set for each nonterminal.

  Predict contains all Terminals that may be the first terminal read in a
  sentential form from that nonterminal.
  */
  void create_predict() {
    predict_.clear();
    const TranslationGrammar &tg = *translationGrammar_;
    for (auto &r : tg.rules()) {
      vector<Symbol> compoundFirst;
      vector<Symbol> rfollow = follow_[tg.nonterminal_index(r.nonterminal())];
      bool compoundEmpty = true;
      for (auto &s : reverse(r.input())) {
        size_t i;
        switch (s.type()) {
          case Symbol::Type::TERMINAL:
            compoundEmpty = false;
            compoundFirst = vector<Symbol>({s});
            break;
          case Symbol::Type::NONTERMINAL:
            i = tg.nonterminal_index(s);
            if (!empty_[i]) {
              compoundEmpty = false;
              compoundFirst = first_[i];
            } else {
              modify_set(compoundFirst, first_[i]);
            }
          default:
            break;
        }
      }
      predict_.push_back(compoundFirst);

      if (compoundEmpty) {
        modify_set(predict_.back(), rfollow);
      }
    }  // for all rules
  }

  /**
  \brief Creates iterator attribute actions for incoming terminals.

  \param[in] obegin Iterator to the first Symbol of the output of the applied
  Rule.
  \param[in] targets Indices of the target actions for all input terminals.
  \param[out] attributeActions Targets to append incoming terminal's attributes.

  The added iterators point to input terminal attribute targets.
  */
  void create_attibute_actions(
      tstack<Symbol>::iterator obegin, const vector<set<size_t>> &targets,
      tstack<vector<tstack<Symbol>::iterator>> &attributeActions) {
    for (auto &target : reverse(targets)) {
      vector<tstack<Symbol>::iterator> iterators;
      for (auto &i : target) {
        auto oit = obegin;
        for (size_t x = 0; x < i; ++x)
          ++oit;
        if (oit->type() == Symbol::Type::TERMINAL)
          iterators.push_back(oit);
      }
      attributeActions.push(iterators);
    }
  }

 public:
  /**
  \brief Constructs a LLTranslationControl.
  */
  LLTranslationControl() = default;
  /**
  \brief Default destructor.
  */
  virtual ~LLTranslationControl() = default;
  /**
  \brief Constructs LLTranslationControl with a LexicalAnalyzer and
  TranslationGrammar.

  \param[in] la A reference to the lexical analyzer to be used to get tokens.
  \param[in] tg The translation grammar for this translation.
  */
  LLTranslationControl(LexicalAnalyzer &la, TranslationGrammar &tg) {
    set_grammar(tg);
    set_lexical_analyzer(la);
  }

  /**
  \brief Sets translation grammar.

  \param[in] tg The translation grammar for this translation.
  */
  virtual void set_grammar(const TranslationGrammar &tg) {
    translationGrammar_ = &tg;
    create_ll_table();
  }

  /**
  \brief Runs the translation. Output symbols are stored in output_.
  */
  virtual void run() {
    using Type = Symbol::Type;

    if (!lexicalAnalyzer_)
      throw TranslationException("No lexical analyzer was attached.");
    else if (!translationGrammar_)
      throw TranslationException("No translation grammar was attached.");

    input_.clear();
    output_.clear();
    tstack<vector<tstack<Symbol>::iterator>> attributeActions;

    Symbol token = next_token();

    input_.push(Symbol::eof());
    output_.push(Symbol::eof());
    input_.push(translationGrammar_->starting_symbol());
    output_.push(translationGrammar_->starting_symbol());

    // iterator to the first symbol of the last inserted string
    // used to speed up output_ linear search
    auto obegin = output_.begin();

    while (1) {
      Symbol &top = input_.top();
      size_t ruleIndex;
      switch (top.type()) {
        case Type::EOI:
          if (token == Symbol::eof()) {
            return;
          } else {
            add_error(top, token);
            return;
          }
          break;
        case Type::TERMINAL:
          if (top == token) {
            for (auto it : attributeActions.pop()) {
              it->set_attribute(token);
            }
            input_.pop();
            token = next_token();
          } else {
            add_error(top, token);
            if (!error_recovery(token))
              return;
          }
          break;
        case Type::NONTERMINAL:
          lastNonterminal_ = top;
          ruleIndex = llTable_.rule_index(top, token);
          if (ruleIndex < translationGrammar_->rules().size()) {
            auto &rule = translationGrammar_->rules()[ruleIndex];

            obegin = output_.replace(top, rule.output(), obegin);
            input_.replace(input_.begin(), rule.input());
            create_attibute_actions(obegin, rule.actions(), attributeActions);
          } else {
            add_error(top, token);
            if (!error_recovery(token))
              return;
          }
          break;
        default:
          // unexpected symbol type on input stack
          input_.pop();
          break;
      }
    }
  }

  /**
  \brief Adds error message caused by a top symbol and incoming token
  combination.

  \param[in] top The current top symbol.
  \param[in] token The incoming token.
  */
  virtual void add_error(const Symbol &top, const Symbol &token) {
    using Type = Symbol::Type;

    errorFlag_ = true;
    errorString_ += token.location().to_string() + ": ";
    switch (top.type()) {
      case Type::EOI:
        errorString_ += "Unexpected token '" + token.name() +
                        "' after translation has finished.";
        break;
      case Type::TERMINAL:
        errorString_ += "Unexpected token '" + token.name() + "'; expected '" +
                        top.name() + "'";
        break;
      case Type::NONTERMINAL:
        // TODO list expected tokens
        errorString_ += "Unexpected token '" + token.name() +
                        "', nonterminal '" + top.name() + "'";
        break;
      default:
        break;
    }
    errorString_ += "\n";
  }

  /**
  \brief Hartmann error recovery.

  \param[out] token The next valid token.
  \returns True if the error recovery succeeded.
  */
  virtual bool error_recovery(Symbol& token) {
    size_t ruleIndex = 0;
    size_t ntIndex = translationGrammar_.nonterminal_index(lastNonterminal_);
    auto& ntFollow = follow_[ntIndex];
    // get a token from follow(lastNonterminal_)
    while(!is_in(ntFollow, token)) {
      token = next_token();
    }
    // pop stack until a rule is applicable or the same token is on top
    while(true) {
      Symbol& top = input_.top();
      switch(top.type()) {
        case Type::EOI:
          return true;
        case Type::TERMINAL:
          if (top == token)
            return true;
          break;
        case Type::NONTERMINAL:
          ruleIndex = llTable_.rule_index(top, token);
          if (ruleIndex < translationGrammar_->rules().size()) {
            return true;
          }
          break;
        default:
          break;
      }
      input_.pop();
    }

  }
  /**
  \brief Get error message.

  \returns The error message string.
  */
  string error_message() { return errorString_; }
};
}  // namespace ctf
#endif
/*** End of file ll_translation_control.hpp ***/<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <iostream>
#include <boost/optional.hpp>

#include "Utils.hpp"
#include "PerfsTimer.hpp"
#include "Options.hpp"
#include "likely.hpp"

#include "ltac/PeepholeOptimizer.hpp"
#include "ltac/Printer.hpp"

#include "mtac/Utils.hpp"

using namespace eddic;

namespace {

template<typename T>
inline bool is_reg(T value){
    return mtac::is<ltac::Register>(value);
}

inline bool transform_to_nop(std::shared_ptr<ltac::Instruction> instruction){
    if(instruction->op == ltac::Operator::NOP){
        return false;
    }

    instruction->op = ltac::Operator::NOP;
    instruction->arg1.reset();
    instruction->arg2.reset();

    return true;
}

inline bool optimize_statement(ltac::Statement& statement){
    if(boost::get<std::shared_ptr<ltac::Instruction>>(&statement)){
        auto instruction = boost::get<std::shared_ptr<ltac::Instruction>>(statement);

        if(instruction->op == ltac::Operator::MOV){
            //MOV reg, 0 can be transformed into XOR reg, reg
            if(is_reg(*instruction->arg1) && mtac::equals<int>(*instruction->arg2, 0)){
                instruction->op = ltac::Operator::XOR;
                instruction->arg2 = instruction->arg1;

                return true;
            }

            if(is_reg(*instruction->arg1) && is_reg(*instruction->arg2)){
                auto& reg1 = boost::get<ltac::Register>(*instruction->arg1);
                auto& reg2 = boost::get<ltac::Register>(*instruction->arg2);

                //MOV reg, reg is useless
                if(reg1 == reg2){
                    return transform_to_nop(instruction);
                }
            }
        }

        if(instruction->op == ltac::Operator::ADD){
            //ADD reg, 1 can be transformed into INC reg
            if(is_reg(*instruction->arg1) && mtac::equals<int>(*instruction->arg2, 1)){
                instruction->op = ltac::Operator::INC;
                instruction->arg2.reset();

                return true;
            }

            //ADD reg, -1 can be transformed into DEC reg
            if(is_reg(*instruction->arg1) && mtac::equals<int>(*instruction->arg2, -1)){
                instruction->op = ltac::Operator::DEC;
                instruction->arg2.reset();

                    return true;
            }
        }

        if(instruction->op == ltac::Operator::SUB){
            //SUB reg, 1 can be transformed into DEC reg
            if(is_reg(*instruction->arg1) && mtac::equals<int>(*instruction->arg2, 1)){
                instruction->op = ltac::Operator::DEC;
                instruction->arg2.reset();

                    return true;
            }

            //SUB reg, -1 can be transformed into INC reg
            if(is_reg(*instruction->arg1) && mtac::equals<int>(*instruction->arg2, -1)){
                instruction->op = ltac::Operator::INC;
                instruction->arg2.reset();

                    return true;
            }
        }

        if(instruction->op == ltac::Operator::MUL){
            //Optimize multiplications with SHIFTs or LEAs
            if(is_reg(*instruction->arg1) && mtac::is<int>(*instruction->arg2)){
                int constant = boost::get<int>(*instruction->arg2);

                auto reg = boost::get<ltac::Register>(*instruction->arg1);

                if(isPowerOfTwo(constant)){
                    instruction->op = ltac::Operator::SHIFT_LEFT;
                    instruction->arg2 = powerOfTwo(constant);

                    return true;
                }

                if(constant == 3){
                    instruction->op = ltac::Operator::LEA;
                    instruction->arg2 = ltac::Address(reg, reg, 2, 0);

                    return true;
                }

                if(constant == 5){
                    instruction->op = ltac::Operator::LEA;
                    instruction->arg2 = ltac::Address(reg, reg, 4, 0);

                    return true;
                }

                if(constant == 9){
                    instruction->op = ltac::Operator::LEA;
                    instruction->arg2 = ltac::Address(reg, reg, 8, 0);

                    return true;
                }
            }
        }

        if(instruction->op == ltac::Operator::CMP_INT){
            //Optimize comparisons with 0 with or reg, reg
            if(is_reg(*instruction->arg1) && mtac::equals<int>(*instruction->arg2, 0)){
                instruction->op = ltac::Operator::OR;
                instruction->arg2 = instruction->arg1;

                    return true;
            }
        }
    }

    return false;
}

inline bool multiple_statement_optimizations(ltac::Statement& s1, ltac::Statement& s2){
    if(mtac::is<std::shared_ptr<ltac::Instruction>>(s1) && mtac::is<std::shared_ptr<ltac::Instruction>>(s2)){
        auto& i1 = boost::get<std::shared_ptr<ltac::Instruction>>(s1);
        auto& i2 = boost::get<std::shared_ptr<ltac::Instruction>>(s2);

        //Statements after LEAVE are dead
        if(i1->op == ltac::Operator::LEAVE){
            return transform_to_nop(i2);
        }

        //Combine two FREE STACK into one
        if(i1->op == ltac::Operator::FREE_STACK && i2->op == ltac::Operator::FREE_STACK){
            i1->arg1 = boost::get<int>(*i1->arg1) + boost::get<int>(*i2->arg1);

            return transform_to_nop(i2);
        }

        if(i1->op == ltac::Operator::MOV && i2->op == ltac::Operator::MOV){
            if(is_reg(*i1->arg1) && is_reg(*i1->arg2) && is_reg(*i2->arg1) && is_reg(*i2->arg2)){
                auto reg11 = boost::get<ltac::Register>(*i1->arg1);
                auto reg12 = boost::get<ltac::Register>(*i1->arg2);
                auto reg21 = boost::get<ltac::Register>(*i2->arg1);
                auto reg22 = boost::get<ltac::Register>(*i2->arg2);

                //cross MOV (ir4 = ir5, ir5 = ir4), keep only the first
                if (reg11 == reg22 && reg12 == reg21){
                    return transform_to_nop(i2);
                }
            } else if(is_reg(*i1->arg1) && is_reg(*i2->arg1)){
                auto reg11 = boost::get<ltac::Register>(*i1->arg1);
                auto reg21 = boost::get<ltac::Register>(*i2->arg1);

                //Two MOV to the same register => keep only last MOV
                if(reg11 == reg21){
                    return transform_to_nop(i1);
                }
            } else if(is_reg(*i1->arg1) && is_reg(*i2->arg2)){
                if(boost::get<ltac::Address>(&*i1->arg2) && boost::get<ltac::Address>(&*i2->arg1)){
                    if(boost::get<ltac::Address>(*i1->arg2) == boost::get<ltac::Address>(*i2->arg1)){
                        return transform_to_nop(i2);
                    }
                }
            } else if(is_reg(*i1->arg2) && is_reg(*i2->arg1)){
                if(boost::get<ltac::Address>(&*i1->arg1) && boost::get<ltac::Address>(&*i2->arg2)){
                    if(boost::get<ltac::Address>(*i1->arg1) == boost::get<ltac::Address>(*i2->arg2)){
                        return transform_to_nop(i2);
                    }
                }
            }
        }

        if(i1->op == ltac::Operator::MOV && i2->op == ltac::Operator::ADD){
            if(is_reg(*i1->arg1) && is_reg(*i2->arg1)){
                if(boost::get<ltac::Register>(*i1->arg1) == boost::get<ltac::Register>(*i2->arg1)){
                    if(boost::get<ltac::Register>(&*i1->arg2) && boost::get<int>(&*i2->arg2)){
                        i2->op = ltac::Operator::LEA;
                        i2->arg2 = ltac::Address(boost::get<ltac::Register>(*i1->arg2), boost::get<int>(*i2->arg2));

                        return transform_to_nop(i1);
                    } else if(boost::get<std::string>(&*i1->arg2) && boost::get<int>(&*i2->arg2)){
                        i2->op = ltac::Operator::LEA;
                        i2->arg2 = ltac::Address(boost::get<std::string>(*i1->arg2), boost::get<int>(*i2->arg2));

                        return transform_to_nop(i1);
                    }
                }
            }
        }
    }

    return false;
}

inline bool is_nop(ltac::Statement& statement){
    if(mtac::is<std::shared_ptr<ltac::Instruction>>(statement)){
        auto instruction = boost::get<std::shared_ptr<ltac::Instruction>>(statement);

        if(instruction->op == ltac::Operator::NOP){
            return true;
        }
    }

    return false;
}

bool basic_optimizations(std::shared_ptr<ltac::Function> function){
    auto& statements = function->getStatements();

    auto it = statements.begin();
    auto end = statements.end() - 1;

    bool optimized = false;

    while(it != end){
        auto& s1 = *it;
        auto& s2 = *(it + 1);

        //Optimizations that looks at only one statement
        optimized |= optimize_statement(s1);
        optimized |= optimize_statement(s2);

        //Optimizations that looks at several statements at once
        optimized |= multiple_statement_optimizations(s1, s2);

        if(unlikely(is_nop(s1))){
            it = statements.erase(it);
            end = statements.end() - 1;

            continue;
        }

        ++it;
    }

    return optimized;
}

bool constant_propagation(std::shared_ptr<ltac::Function> function){
    bool optimized = false;

    auto& statements = function->getStatements();

    std::size_t bb = 0;

    std::unordered_map<ltac::Register, int, ltac::RegisterHash> constants;

    while(bb < statements.size()){
        std::size_t i = bb;
        for(; i < statements.size(); ++i){
            auto statement = statements[i];

            if(auto* ptr = boost::get<std::shared_ptr<ltac::Instruction>>(&statement)){
                auto instruction = *ptr;

                //Erase constant
                if(instruction->arg1 && is_reg(*instruction->arg1)){
                    auto reg1 = boost::get<ltac::Register>(*instruction->arg1);

                    constants.erase(reg1);
                }

                //Collect constants
                if(instruction->op == ltac::Operator::XOR){
                    if(is_reg(*instruction->arg1) && is_reg(*instruction->arg2)){
                        auto reg1 = boost::get<ltac::Register>(*instruction->arg1);
                        auto reg2 = boost::get<ltac::Register>(*instruction->arg2);

                        if(reg1 == reg2){
                            constants[reg1] = 0;
                        }
                    }
                } else if(instruction->op == ltac::Operator::MOV){
                    if(is_reg(*instruction->arg1)){
                        if (auto* valuePtr = boost::get<int>(&*instruction->arg2)){
                            auto reg1 = boost::get<ltac::Register>(*instruction->arg1);
                            constants[reg1] = *valuePtr;
                        }
                    }
                }

                //Optimize MOV
                if(instruction->op == ltac::Operator::MOV){
                    if(is_reg(*instruction->arg2)){
                        auto reg2 = boost::get<ltac::Register>(*instruction->arg2);

                        if(constants.find(reg2) != constants.end()){
                            instruction->arg2 = constants[reg2];
                            optimized = true;
                        }
                    }
                }
            } else {
                //At this point, the basic block is at its end
                break;
            }
        }

        //Start optimizations for the next basic block
        bb = i + 1;
        constants.clear();
    }

    return optimized;
}

bool debug(const std::string& name, bool b, std::shared_ptr<ltac::Function> function){
    if(option_defined("dev")){
        if(b){
            std::cout << "optimization " << name << " returned true" << std::endl;

            //Print the function
            ltac::Printer printer;
            printer.print(function);
        } else {
            std::cout << "optimization " << name << " returned false" << std::endl;
        }
    }

    return b;
}

} //end of anonymous namespace

void eddic::ltac::optimize(std::shared_ptr<ltac::Program> program){
    PerfsTimer timer("Peephole optimizations");

    for(auto& function : program->functions){
        if(option_defined("dev")){
            std::cout << "Start optimizations on " << function->getName() << std::endl;

            //Print the function
            ltac::Printer printer;
            printer.print(function);
        }

        bool optimized;
        do {
            optimized = false;

            optimized |= debug("Basic optimizations", basic_optimizations(function), function);
            optimized |= debug("Constant propagation", constant_propagation(function), function);
        } while(optimized);
    }
}
<commit_msg>Cleanup<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <iostream>
#include <boost/optional.hpp>

#include "Utils.hpp"
#include "PerfsTimer.hpp"
#include "Options.hpp"
#include "likely.hpp"

#include "ltac/PeepholeOptimizer.hpp"
#include "ltac/Printer.hpp"

#include "mtac/Utils.hpp"

using namespace eddic;

namespace {

template<typename T>
inline bool is_reg(T value){
    return mtac::is<ltac::Register>(value);
}

inline bool transform_to_nop(std::shared_ptr<ltac::Instruction> instruction){
    if(instruction->op == ltac::Operator::NOP){
        return false;
    }

    instruction->op = ltac::Operator::NOP;
    instruction->arg1.reset();
    instruction->arg2.reset();

    return true;
}

inline bool optimize_statement(ltac::Statement& statement){
    if(boost::get<std::shared_ptr<ltac::Instruction>>(&statement)){
        auto instruction = boost::get<std::shared_ptr<ltac::Instruction>>(statement);

        if(instruction->op == ltac::Operator::MOV){
            //MOV reg, 0 can be transformed into XOR reg, reg
            if(is_reg(*instruction->arg1) && mtac::equals<int>(*instruction->arg2, 0)){
                instruction->op = ltac::Operator::XOR;
                instruction->arg2 = instruction->arg1;

                return true;
            }

            if(is_reg(*instruction->arg1) && is_reg(*instruction->arg2)){
                auto& reg1 = boost::get<ltac::Register>(*instruction->arg1);
                auto& reg2 = boost::get<ltac::Register>(*instruction->arg2);

                //MOV reg, reg is useless
                if(reg1 == reg2){
                    return transform_to_nop(instruction);
                }
            }
        }

        if(instruction->op == ltac::Operator::ADD){
            //ADD reg, 1 can be transformed into INC reg
            if(is_reg(*instruction->arg1) && mtac::equals<int>(*instruction->arg2, 1)){
                instruction->op = ltac::Operator::INC;
                instruction->arg2.reset();

                return true;
            }

            //ADD reg, -1 can be transformed into DEC reg
            if(is_reg(*instruction->arg1) && mtac::equals<int>(*instruction->arg2, -1)){
                instruction->op = ltac::Operator::DEC;
                instruction->arg2.reset();

                    return true;
            }
        }

        if(instruction->op == ltac::Operator::SUB){
            //SUB reg, 1 can be transformed into DEC reg
            if(is_reg(*instruction->arg1) && mtac::equals<int>(*instruction->arg2, 1)){
                instruction->op = ltac::Operator::DEC;
                instruction->arg2.reset();

                    return true;
            }

            //SUB reg, -1 can be transformed into INC reg
            if(is_reg(*instruction->arg1) && mtac::equals<int>(*instruction->arg2, -1)){
                instruction->op = ltac::Operator::INC;
                instruction->arg2.reset();

                    return true;
            }
        }

        if(instruction->op == ltac::Operator::MUL){
            //Optimize multiplications with SHIFTs or LEAs
            if(is_reg(*instruction->arg1) && mtac::is<int>(*instruction->arg2)){
                int constant = boost::get<int>(*instruction->arg2);

                auto reg = boost::get<ltac::Register>(*instruction->arg1);

                if(isPowerOfTwo(constant)){
                    instruction->op = ltac::Operator::SHIFT_LEFT;
                    instruction->arg2 = powerOfTwo(constant);

                    return true;
                }

                if(constant == 3){
                    instruction->op = ltac::Operator::LEA;
                    instruction->arg2 = ltac::Address(reg, reg, 2, 0);

                    return true;
                }

                if(constant == 5){
                    instruction->op = ltac::Operator::LEA;
                    instruction->arg2 = ltac::Address(reg, reg, 4, 0);

                    return true;
                }

                if(constant == 9){
                    instruction->op = ltac::Operator::LEA;
                    instruction->arg2 = ltac::Address(reg, reg, 8, 0);

                    return true;
                }
            }
        }

        if(instruction->op == ltac::Operator::CMP_INT){
            //Optimize comparisons with 0 with or reg, reg
            if(is_reg(*instruction->arg1) && mtac::equals<int>(*instruction->arg2, 0)){
                instruction->op = ltac::Operator::OR;
                instruction->arg2 = instruction->arg1;

                    return true;
            }
        }
    }

    return false;
}

inline bool multiple_statement_optimizations(ltac::Statement& s1, ltac::Statement& s2){
    if(mtac::is<std::shared_ptr<ltac::Instruction>>(s1) && mtac::is<std::shared_ptr<ltac::Instruction>>(s2)){
        auto& i1 = boost::get<std::shared_ptr<ltac::Instruction>>(s1);
        auto& i2 = boost::get<std::shared_ptr<ltac::Instruction>>(s2);

        //Statements after LEAVE are dead
        if(i1->op == ltac::Operator::LEAVE){
            return transform_to_nop(i2);
        }

        //Combine two FREE STACK into one
        if(i1->op == ltac::Operator::FREE_STACK && i2->op == ltac::Operator::FREE_STACK){
            i1->arg1 = boost::get<int>(*i1->arg1) + boost::get<int>(*i2->arg1);

            return transform_to_nop(i2);
        }

        if(i1->op == ltac::Operator::MOV && i2->op == ltac::Operator::MOV){
            if(is_reg(*i1->arg1) && is_reg(*i1->arg2) && is_reg(*i2->arg1) && is_reg(*i2->arg2)){
                auto reg11 = boost::get<ltac::Register>(*i1->arg1);
                auto reg12 = boost::get<ltac::Register>(*i1->arg2);
                auto reg21 = boost::get<ltac::Register>(*i2->arg1);
                auto reg22 = boost::get<ltac::Register>(*i2->arg2);

                //cross MOV (ir4 = ir5, ir5 = ir4), keep only the first
                if (reg11 == reg22 && reg12 == reg21){
                    return transform_to_nop(i2);
                }
            } else if(is_reg(*i1->arg1) && is_reg(*i2->arg1)){
                auto reg11 = boost::get<ltac::Register>(*i1->arg1);
                auto reg21 = boost::get<ltac::Register>(*i2->arg1);

                //Two MOV to the same register => keep only last MOV
                if(reg11 == reg21){
                    return transform_to_nop(i1);
                }
            } else if(is_reg(*i1->arg1) && is_reg(*i2->arg2)){
                if(boost::get<ltac::Address>(&*i1->arg2) && boost::get<ltac::Address>(&*i2->arg1)){
                    if(boost::get<ltac::Address>(*i1->arg2) == boost::get<ltac::Address>(*i2->arg1)){
                        return transform_to_nop(i2);
                    }
                }
            } else if(is_reg(*i1->arg2) && is_reg(*i2->arg1)){
                if(boost::get<ltac::Address>(&*i1->arg1) && boost::get<ltac::Address>(&*i2->arg2)){
                    if(boost::get<ltac::Address>(*i1->arg1) == boost::get<ltac::Address>(*i2->arg2)){
                        return transform_to_nop(i2);
                    }
                }
            }
        }

        if(i1->op == ltac::Operator::MOV && i2->op == ltac::Operator::ADD){
            if(is_reg(*i1->arg1) && is_reg(*i2->arg1)){
                if(boost::get<ltac::Register>(*i1->arg1) == boost::get<ltac::Register>(*i2->arg1)){
                    if(boost::get<ltac::Register>(&*i1->arg2) && boost::get<int>(&*i2->arg2)){
                        i2->op = ltac::Operator::LEA;
                        i2->arg2 = ltac::Address(boost::get<ltac::Register>(*i1->arg2), boost::get<int>(*i2->arg2));

                        return transform_to_nop(i1);
                    } else if(boost::get<std::string>(&*i1->arg2) && boost::get<int>(&*i2->arg2)){
                        i2->op = ltac::Operator::LEA;
                        i2->arg2 = ltac::Address(boost::get<std::string>(*i1->arg2), boost::get<int>(*i2->arg2));

                        return transform_to_nop(i1);
                    }
                }
            }
        }
    }

    return false;
}

inline bool is_nop(ltac::Statement& statement){
    if(mtac::is<std::shared_ptr<ltac::Instruction>>(statement)){
        auto instruction = boost::get<std::shared_ptr<ltac::Instruction>>(statement);

        if(instruction->op == ltac::Operator::NOP){
            return true;
        }
    }

    return false;
}

bool basic_optimizations(std::shared_ptr<ltac::Function> function){
    auto& statements = function->getStatements();

    auto it = statements.begin();
    auto end = statements.end() - 1;

    bool optimized = false;

    while(it != end){
        auto& s1 = *it;
        auto& s2 = *(it + 1);

        //Optimizations that looks at only one statement
        optimized |= optimize_statement(s1);
        optimized |= optimize_statement(s2);

        //Optimizations that looks at several statements at once
        optimized |= multiple_statement_optimizations(s1, s2);

        if(unlikely(is_nop(s1))){
            it = statements.erase(it);
            end = statements.end() - 1;

            continue;
        }

        ++it;
    }

    return optimized;
}

bool constant_propagation(std::shared_ptr<ltac::Function> function){
    bool optimized = false;

    auto& statements = function->getStatements();

    std::unordered_map<ltac::Register, int, ltac::RegisterHash> constants;

    for(std::size_t i = 0; i < statements.size(); ++i){
        auto statement = statements[i];

        if(auto* ptr = boost::get<std::shared_ptr<ltac::Instruction>>(&statement)){
            auto instruction = *ptr;

            //Erase constant
            if(instruction->arg1 && is_reg(*instruction->arg1)){
                auto reg1 = boost::get<ltac::Register>(*instruction->arg1);

                constants.erase(reg1);
            }

            //Collect constants
            if(instruction->op == ltac::Operator::XOR){
                if(is_reg(*instruction->arg1) && is_reg(*instruction->arg2)){
                    auto reg1 = boost::get<ltac::Register>(*instruction->arg1);
                    auto reg2 = boost::get<ltac::Register>(*instruction->arg2);

                    if(reg1 == reg2){
                        constants[reg1] = 0;
                    }
                }
            } else if(instruction->op == ltac::Operator::MOV){
                if(is_reg(*instruction->arg1)){
                    if (auto* valuePtr = boost::get<int>(&*instruction->arg2)){
                        auto reg1 = boost::get<ltac::Register>(*instruction->arg1);
                        constants[reg1] = *valuePtr;
                    }
                }
            }

            //Optimize MOV
            if(instruction->op == ltac::Operator::MOV){
                if(is_reg(*instruction->arg2)){
                    auto reg2 = boost::get<ltac::Register>(*instruction->arg2);

                    if(constants.find(reg2) != constants.end()){
                        instruction->arg2 = constants[reg2];
                        optimized = true;
                    }
                }
            }
        } else {
            //At this point, the basic block is at its end
            constants.clear();
        }
    }

    return optimized;
}

bool debug(const std::string& name, bool b, std::shared_ptr<ltac::Function> function){
    if(option_defined("dev")){
        if(b){
            std::cout << "optimization " << name << " returned true" << std::endl;

            //Print the function
            ltac::Printer printer;
            printer.print(function);
        } else {
            std::cout << "optimization " << name << " returned false" << std::endl;
        }
    }

    return b;
}

} //end of anonymous namespace

void eddic::ltac::optimize(std::shared_ptr<ltac::Program> program){
    PerfsTimer timer("Peephole optimizations");

    for(auto& function : program->functions){
        if(option_defined("dev")){
            std::cout << "Start optimizations on " << function->getName() << std::endl;

            //Print the function
            ltac::Printer printer;
            printer.print(function);
        }

        bool optimized;
        do {
            optimized = false;

            optimized |= debug("Basic optimizations", basic_optimizations(function), function);
            optimized |= debug("Constant propagation", constant_propagation(function), function);
        } while(optimized);
    }
}
<|endoftext|>