<commit_before>/**
 * @param {string} code 
 */
module.exports.JSEvaluator = function(code){
		try {
			eval(code)
		} catch (SyntaxError) {
			//todo
		}
}

const identifier = "6q3co7"
const jsonErrorIdentifier = "6q3co6"

module.exports.PythonEvaluator = class{

	
	/**
	 * starts pythonEvaluator.py
	 */
	constructor(){
		this.running = false
		this.PythonShell = require('python-shell')

		let rootPath = process.cwd()
		if(rootPath.endsWith("arepl-win32-x64")){
		  rootPath = rootPath + '/resources/app'
		}

		this.pythonEvalFolderPath = rootPath + '/src/python/'

		// for non-windows OS it is best to use python3 instead of python
		// Mac and Ubuntu both have python being v2 by default
		// archlinux and freebsd both use v3 as default, but also provide python3 command
		this.pythonPath = process.platform != "win32" ? "python3" : "python"

		this.startPython()
	}

	
	/**
	 * does not do anything if program is already running
	 * @param {{evalCode:string}} code 
	 */
	execCode(code){
		if(this.running) return
		this.running = true
		this.pyshell.send(JSON.stringify(code))
	}

	/**
	 * @param {string} message 
	 */
	sendStdin(message){
		this.pyshell.send(message)
	}

	/**
	 * kills python process and restarts.  Force-kills if necessary.
	 */
	restart(){
		
		var restarting = false

		// register callback for restart
		// using childProcess callback instead of pyshell callback
		// (pyshell callback only happens when process exits voluntarily)
		this.pyshell.childProcess.on('exit',()=>{
			restarting = true
			this.startPython()
		})

		// pyshell has 50 ms to die gracefully
		var dead = this.pyshell.childProcess.kill()
		if(!dead) console.info("pyshell refused to die")

		setTimeout(()=>{
			if(!dead && !restarting){
				// murder the process with extreme prejudice
				dead = this.pyshell.childProcess.kill('SIGKILL')
				if(!dead){
					console.error("the python process simply cannot be killed!")
				}
			}
		}, 50)
	}

	startPython(){
		console.log("Starting Python...")
		this.pyshell = new this.PythonShell('pythonEvaluator.py', {
			scriptPath: this.pythonEvalFolderPath,
			pythonPath: this.pythonPath,
		})
		this.pyshell.on('message', message => {
			console.debug(message)
			this.handleResult(message)
		})
	}

	/**
	 * Overwrite this with your own handler.
	 * is called when program fails or completes
	 * @param {{ERROR:string, userVariables:Object}} foo
	 */
	onResult(foo){}

	/**
	 * Overwrite this with your own handler.
	 * Is called when program prints
	 * @param {string} foo
	 */
	onPrint(foo){}

	/**
	 * handles pyshell results and calls onResult / onPrint
	 * @param {string} results 
	 */
	handleResult(results) {
		this.running = false
		let pyResult = {
			"ERROR":"",
			"userVariables": ""
		}

        //if result should have identifier, otherwise it is just a printout from users code
        if(results.startsWith(identifier)){
            results = results.replace(identifier,"")
            pyResult = JSON.parse(results)

			pyResult.userVariables = JSON.parse(pyResult.userVariables)

            if(pyResult.ERROR != ""){
                pyResult.ERROR = this.formatPythonException(pyResult.ERROR)
            }
		}
		else if(results.startsWith(jsonErrorIdentifier)){
			results = results.replace(jsonErrorIdentifier)
			console.warn("error in python evaluator converting stdin to JSON. " +
			"User probably just sent stdin without input() in his program.\n" + results)
		}
        else{
            this.onPrint(results)
        }

        this.onResult(pyResult)
	}

	/**
	 * gets rid of unnecessary exception data, among other things
	 * @param {string} err
	 * @example err:
	 * "Traceback (most recent call last):
	 *   File "pythonEvaluator.py", line 26, in <module>
	 * 	exec(data['evalCode'], evalLocals)
	 *   line 4, in <module>
	 * NameError: name 'y' is not defined"
	 * @returns {string}
	 */
	formatPythonException(err){
	
		//unescape newlines
		err = err.replace(/\\n/g, "\n")
	
		if(err.startsWith("There has been a error when trying to display your variables")){
			// formatting would not work for this exception because it happens outside of exec()<commit_msg>fixed some issues i found<commit_after>/**
 * @param {string} code 
 */
module.exports.JSEvaluator = function(code){
		try {
			eval(code)
		} catch (SyntaxError) {
			//todo
		}
}

const identifier = "6q3co7"
const jsonErrorIdentifier = "6q3co6"

module.exports.PythonEvaluator = class{

	
	/**
	 * starts pythonEvaluator.py
	 */
	constructor(){
		this.running = false
		this.PythonShell = require('python-shell')

		let rootPath = process.cwd()
		if(rootPath.endsWith("arepl-win32-x64")){
		  rootPath = rootPath + '/resources/app'
		}

		this.pythonEvalFolderPath = rootPath + '/src/python/'

		// for non-windows OS it is best to use python3 instead of python
		// Mac and Ubuntu both have python being v2 by default
		// archlinux and freebsd both use v3 as default, but also provide python3 command
		this.pythonPath = process.platform != "win32" ? "python3" : "python"

		this.startPython()
	}

	
	/**
	 * does not do anything if program is already running
	 * @param {{evalCode:string}} code 
	 */
	execCode(code){
		if(this.running) return
		this.running = true
		this.pyshell.send(JSON.stringify(code))
	}

	/**
	 * @param {string} message 
	 */
	sendStdin(message){
		this.pyshell.send(message)
	}

	/**
	 * kills python process and restarts.  Force-kills if necessary.
	 */
	restart(){
		
		var restarting = false

		// register callback for restart
		// using childProcess callback instead of pyshell callback
		// (pyshell callback only happens when process exits voluntarily)
		this.pyshell.childProcess.on('exit',()=>{
			restarting = true
			this.startPython()
		})

		// pyshell has 50 ms to die gracefully
		var dead = this.pyshell.childProcess.kill()
		if(!dead) console.info("pyshell refused to die")

		setTimeout(()=>{
			if(!dead && !restarting){
				// murder the process with extreme prejudice
				dead = this.pyshell.childProcess.kill('SIGKILL')
				if(!dead){
					console.error("the python process simply cannot be killed!")
				}
			}
		}, 50)
	}

	startPython(){
		console.log("Starting Python...")
		this.pyshell = new this.PythonShell('pythonEvaluator.py', {
			scriptPath: this.pythonEvalFolderPath,
			pythonPath: this.pythonPath,
		})
		this.pyshell.on('message', message => {
			console.info(message)
			this.handleResult(message)
		})
	}

	/**
	 * Overwrite this with your own handler.
	 * is called when program fails or completes
	 * @param {{ERROR:string, userVariables:Object}} foo
	 */
	onResult(foo){}

	/**
	 * Overwrite this with your own handler.
	 * Is called when program prints
	 * @param {string} foo
	 */
	onPrint(foo){}

	/**
	 * handles pyshell results and calls onResult / onPrint
	 * @param {string} results 
	 */
	handleResult(results) {
		let pyResult = {
			"ERROR":"",
			"userVariables": ""
		}

        //result should have identifier, otherwise it is just a printout from users code
        if(results.startsWith(identifier)){
			this.running = false
            results = results.replace(identifier,"")
            pyResult = JSON.parse(results)
			pyResult.userVariables = JSON.parse(pyResult.userVariables)

            if(pyResult.ERROR != ""){
                pyResult.ERROR = this.formatPythonException(pyResult.ERROR)
			}

			this.onResult(pyResult)
		}
		else if(results.startsWith(jsonErrorIdentifier)){
			results = results.replace(jsonErrorIdentifier)
			console.warn("error in python evaluator converting stdin to JSON. " +
			"User probably just sent stdin without input() in his program.\n" + results)
		}
        else{
			// get rid of \r at end
			results = results.slice(0, results.length-1);
            this.onPrint(results)
        }
	}

	/**
	 * gets rid of unnecessary exception data, among other things
	 * @param {string} err
	 * @example err:
	 * "Traceback (most recent call last):
	 *   File "pythonEvaluator.py", line 26, in <module>
	 * 	exec(data['evalCode'], evalLocals)
	 *   line 4, in <module>
	 * NameError: name 'y' is not defined"
	 * @returns {string}
	 */
	formatPythonException(err){
	
		//unescape newlines
		err = err.replace(/\\n/g, "\n")
	
		if(err.startsWith("There has been a error when trying to display your variables")){
			// formatting would not work for this exception because it happens outside of exec()
{'fraction_non_alphanumeric': 0.12505972288580985, 'fraction_numerical': 0.005972288580984233, 'mean_word_length': 3.155334987593052, 'pattern_counts': {'":': 4, '<': 7, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2924478', 'n_tokens_mistral': 2928, 'n_tokens_neox': 2553, 'n_words': 719}