[rkward-cvs] SF.net SVN: rkward:[3140] branches/2010_10_18_backend_restructuring_branch/ rkward/rbackend

tfry at users.sourceforge.net tfry at users.sourceforge.net
Sun Oct 24 12:40:34 UTC 2010


Revision: 3140
          http://rkward.svn.sourceforge.net/rkward/?rev=3140&view=rev
Author:   tfry
Date:     2010-10-24 12:40:33 +0000 (Sun, 24 Oct 2010)

Log Message:
-----------
Rework RReadConsole to become the center of the event loop.
Unfinished, will not compile, ATM.

Modified Paths:
--------------
    branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rembedinternal.cpp
    branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rembedinternal.h

Modified: branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rembedinternal.cpp
===================================================================
--- branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rembedinternal.cpp	2010-10-23 06:50:13 UTC (rev 3139)
+++ branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rembedinternal.cpp	2010-10-24 12:40:33 UTC (rev 3140)
@@ -18,7 +18,8 @@
 #include "rembedinternal.h"
 
 // static
-RThread *RThread::this_pointer = 0; 
+RThread *RThread::this_pointer = 0;
+RThread::RKReplStatus RThread::repl_status = { QByteArray (), 0, true, 0, RThread::RKReplStatus::NoUserCommand, 0 };
 
 #include <qstring.h>
 #include <QStringList>
@@ -96,6 +97,7 @@
 
 // some functions we need that are not declared
 extern void Rf_PrintWarnings (void);
+extern void run_Rmainloop (void);
 SEXP R_LastvalueSymbol;
 #include <R_ext/eventloop.h>
 }
@@ -103,14 +105,6 @@
 #include "../rkglobals.h"
 #include "rdata.h"
 
-// Needed for the REPL
-const char *current_buffer = 0;
-bool repldlldo1_wants_code = false;
-bool repldll_buffer_transfer_finished = false;
-int repldll_result = 0;		/* -2: error; -1: incomplete; 0: nothing, yet 1: ok 2: incomplete statement, while buffer not empty. Should not happen */
-bool repldll_last_parse_successful = false;
-
-
 SEXP RKWard_RData_Tag;
 QString *SEXPToStringList (SEXP from_exp, unsigned int *count);
 QString SEXPToString (SEXP from_exp);
@@ -131,34 +125,121 @@
 	Rf_error ("Backend dead");	// this jumps us out of the REPL.
 }
 
-int RReadConsole (const char* prompt, unsigned char* buf, int buflen, int hist) {
+Rboolean RKToplevelStatementFinishedCallback (SEXP expr, SEXP value, Rboolean succeeded, Rboolean visible, void *) {
 	RK_TRACE (RBACKEND);
+	Q_UNUSED (expr);
+	Q_UNUSED (value);
+	Q_UNUSED (visible);
 
-	// handle requests for new code
-	if (repldlldo1_wants_code) {
-		//Rprintf ("wants code, buffsize: %d\n", buflen);
-		if (repldll_buffer_transfer_finished) {
-			return 0;
+	if (RThread::repl_status.eval_depth == 0) {
+		RK_ASSERT (RThread::repl_status.user_command_status = RThread::RKReplStatus::UserCommandRunning);
+		if (succeeded) {
+			RThread::repl_status.user_command_successful_up_to = RThread::repl_status.user_command_transmitted_up_to;
+			if (RThread::repl_status.user_command_completely_transmitted) RThread::repl_status.user_command_status = RThread::RKReplStatus::NoUserCommand;
+			else RThread::repl_status.user_command_status = RThread::RKReplStatus::UserCommandTransmitted;
+		} else {
+			// skip remainder of command
+			RThread::repl_status.user_command_status = RThread::RKReplStatus::NoUserCommand;
 		}
+	}
+	
+	return (Rboolean) true;
+}
 
-		int pos = 0;		// fgets emulation
-		while (pos < (buflen-1)) {
-			buf[pos] = *current_buffer;
-			if (*current_buffer == '\0') {
-				repldll_buffer_transfer_finished = true;
-				break;
+void RKInsertToplevelStatementFinishedCallback (void *) {
+	RK_TRACE (RBACKEND);
+
+	if (RThread::this_pointer->r_running) {
+		int pos;
+		Rf_addTaskCallback (&RKToplevelStatementFinishedCallback, 0, &RKInsertToplevelStatementFinishedCallback, "_rkward_main_callback", &pos);
+	}
+}
+
+void RKTransmitNextUserCommandChunk (unsigned char* buf, int buflen) {
+	RK_TRACE (RBACKEND);
+
+	RK_ASSERT (RThread::repl_status.user_command_transmitted_up_to < RThread::repl_status.user_command_buffer.length ());
+	const char* current_buffer = RThread::repl_status.user_command_buffer.data ();
+	current_buffer += RThread::repl_status.user_command_transmitted_up_to;	// Skip what we have already transmitted
+
+	bool reached_eof = false;
+	int pos = 0;
+	while (pos < (buflen-1)) {
+		buf[pos] = *current_buffer;
+		if (*current_buffer == '\n') break;
+		else if (*current_buffer == ';') break;
+		else if (*current_buffer == '\0') {
+			reached_eof = true;
+			break;
+		}
+		++current_buffer;
+		++pos;
+	}
+	RThread::repl_status.user_command_transmitted_up_to += (pos + 1);
+	if (reached_eof) {
+		buf[pos] = '\n';
+		RThread::repl_status.user_command_completely_transmitted = true;
+	}
+	buf[++pos] = '\0';
+}
+
+int RReadConsole (const char* prompt, unsigned char* buf, int buflen, int hist) {
+	RK_TRACE (RBACKEND);
+
+	RK_ASSERT (buf && buflen);
+	RK_ASSERT (RThread::repl_status.eval_depth >= 0);
+	if (RThread::repl_status.eval_depth == 0) {
+		if (RThread::repl_status.user_command_status == RThread::RKReplStatus::NoUserCommand) {
+			MUTEX_LOCK;
+			RCommand *command = RThread::this_pointer->fetchNextCommand ();
+			if (!(command->type () & RCommand::User)) {
+				RThread::this_pointer->runCommand (command);
+				MUTEX_UNLOCK;
+			} else {
+				// so, we are about to transmit a new user command, which is quite a complex endeavour...
+				RThread::repl_status.user_command_transmitted_up_to = 0;
+				RThread::repl_status.user_command_completely_transmitted = false;
+				RThread::repl_status.user_command_successful_up_to = 0;
+				RThread::repl_status.user_command_buffer = RThread::this_pointer->current_locale_codec->fromUnicode (command->command ());
+				RThread::this_pointer->current_command = command;
+				MUTEX_UNLOCK;
+				RKTransmitNextUserCommandChunk (buf, buflen);
+				RThread::repl_status.user_command_status = RThread::RKReplStatus::UserCommandTransmitted;
 			}
-			++current_buffer;
-			++pos;
+			buf[0] = '\0';
+			return 1;
+		} else if (RThread::repl_status.user_command_status == RThread::RKReplStatus::UserCommandTransmitted) {
+			if (RThread::repl_status.user_command_completely_transmitted) {
+				// fully transmitted, but R is still asking for more? -> Incomplete statement
+				MUTEX_LOCK;
+				RThread::this_pointer->current_command->status |= RCommand::ErrorIncomplete;
+				RThread::repl_status.user_command_status = RThread::RKReplStatus::NoUserCommand;
+				MUTEX_UNLOCK;
+			} else {
+				RKTransmitNextUserCommandChunk (buf, buflen);
+			}
+			buf[0] = '\0';
+			return 1;
+		} else if (RThread::repl_status.user_command_status == RThread::RKReplStatus::UserCommandSyntaxError) {
+			MUTEX_LOCK;
+			RThread::this_pointer->current_command->status |= RCommand::ErrorIncomplete;
+			RThread::repl_status.user_command_status = RThread::RKReplStatus::NoUserCommand;
+			MUTEX_UNLOCK;
+			buf[0] = '\0';
+			return 1;
+		} else if (RThread::repl_status.user_command_status == RThread::RKReplStatus::UserCommandRunning) {
+			// it appears, the user command triggered a call to readline. Will be handled, below.
+			// NOT returning
+		} else {
+			RK_ASSERT (false);
+			RThread::repl_status.user_command_status = RThread::RKReplStatus::NoUserCommand;
+			buf[0] = '\0';
+			return 1;
 		}
-		if (repldll_buffer_transfer_finished) buf[pos] = '\n';
-		buf[++pos] = '\0';
-		//Rprintf ("buffer now: '%s'\n", buf);
-		return 1;
 	}
-
+	
 	// here, we handle readline() calls and such, i.e. not the regular prompt for code
-	// browser() also takes us here.
+	// browser() also takes us here. TODO: give browser() special handling!
 	RCallbackArgs args;
 	args.type = RCallbackArgs::RReadLine;
 	args.params["prompt"] = QVariant (prompt);
@@ -169,15 +250,17 @@
 	if (args.params["cancelled"].toBool ()) {
 		if (RThread::this_pointer->current_command) RThread::this_pointer->current_command->status |= RCommand::Canceled;
 		RK_doIntr();
-		return 0;	// we should not ever get here, but still...
+		// threoretically, the above should have got us out, but for good measure:
+		Rf_error ("cancelled");
 	}
-	if (buf) {
-		QByteArray localres = RThread::this_pointer->current_locale_codec->fromUnicode (args.params["result"].toString ());
-		// need to append a newline, here. TODO: theoretically, RReadConsole comes back for more, if \0 was encountered before \n.
-		qstrncpy ((char *) buf, localres.left (buflen - 2).append ('\n').data (), buflen);
-		return 1;
-	}
-	return 0;
+
+	QByteArray localres = RThread::this_pointer->current_locale_codec->fromUnicode (args.params["result"].toString ());
+	// need to append a newline, here. TODO: theoretically, RReadConsole comes back for more, if \0 was encountered before \n.
+	qstrncpy ((char *) buf, localres.left (buflen - 2).append ('\n').data (), buflen);
+
+	// we should not ever get here, but still...
+	buf[0] = '\0';
+	return 1;
 }
 
 #ifdef Q_WS_WIN
@@ -189,6 +272,15 @@
 void RWriteConsoleEx (const char *buf, int buflen, int type) {
 	RK_TRACE (RBACKEND);
 
+	// output while nothing else is running (including handlers?) -> This may be a syntax error.
+	if (RThread::repl_status.eval_depth == 0) {
+		if (RThread::repl_status.user_command_status == RThread::RKReplStatus::UserCommandTransmitted) {
+			RThread::repl_status.user_command_status = RThread::RKReplStatus::UserCommandSyntaxError;
+		} else {
+			RK_ASSERT (RThread::repl_status.user_command_status != RThread::RKReplStatus::NoUserCommand);
+		}
+	}
+
 	RThread::this_pointer->handleOutput (RThread::this_pointer->current_locale_codec->toUnicode (buf, buflen), buflen, type == 0);
 }
 
@@ -392,10 +484,11 @@
 void RBusy (int busy) {
 	RK_TRACE (RBACKEND);
 
-	// R_ReplDLLDo1 calls R_Busy (1) after reading in code (if needed), parsing it, and right before evaluating it.
+	// R_ReplIteration calls R_Busy (1) after reading in code (if needed), successfully parsing it, and right before evaluating it.
 	if (busy) {
-		repldlldo1_wants_code = false;
-		repldll_last_parse_successful = true;
+		if (RThread::repl_status.user_command_status == RThread::RKReplStatus::UserCommandTransmitted) {
+			RThread::repl_status.user_command_status = RThread::RKReplStatus::UserCommandRunning;
+		}
 	}
 }
 
@@ -539,8 +632,10 @@
 	// do not trace
 	if (!this_pointer->r_running) return;
 
+	RThread::repl_status.eval_depth++;
 // In case an error (or user interrupt) is caught inside processX11EventsWorker, we don't want to long-jump out.
 	R_ToplevelExec (processX11EventsWorker, 0);
+	RThread::repl_status.eval_depth--;
 }
 
 /** converts SEXP to strings, and returns the first string (or QString(), if SEXP contains no strings) */
@@ -877,6 +972,7 @@
 	R_registerRoutines (R_getEmbeddingDllInfo(), NULL, callMethods, NULL, NULL);
 
 	connectCallbacks();
+	RKInsertToplevelStatementFinishedCallback (0);
 
 	// get info on R runtime version
 	RCommand *dummy = runDirectCommand ("as.numeric (R.version$major) * 1000 + as.numeric (R.version$minor) * 10", RCommand::GetIntVector);
@@ -890,6 +986,12 @@
 	return true;
 }
 
+void RThread::enterEventLoop () {
+	RK_TRACE (RBACKEND);
+
+	run_Rmainloop ();
+}
+
 SEXP parseCommand (const QString &command_qstring, RThread::RKWardRError *error) {
 	RK_TRACE (RBACKEND);
 
@@ -1006,26 +1108,6 @@
 }
 #endif
 
-void runUserCommandInternal (void *) {
-	RK_TRACE (RBACKEND);
-
-/* R_ReplDLLdo1 return codes:
--1: EOF
-1: normal prompt
-2: continuation prompt (parse incomplete) */
-	do {
-		//Rprintf ("iteration status: %d\n", repldll_result);
-		repldll_result = -2;
-		repldlldo1_wants_code = true;
-		repldll_last_parse_successful = false;
-	} while (((repldll_result = R_ReplDLLdo1 ()) == 2) && (!repldll_buffer_transfer_finished));	// keep iterating while the statement is incomplete, and we still have more in the buffer to transfer
-	//Rprintf ("iteration complete, status: %d\n", repldll_result);
-	PROTECT (R_LastvalueSymbol);		// why do we need this? No idea, but R_ToplevelExec tries to unprotect something
-	if (RThread::this_pointer->RRuntimeIsVersion (2, 11, 9)) {
-		PROTECT (R_LastvalueSymbol);		// ... and with R 2.12.0 it tries to unprotect two things
-	}
-}
-
 bool RThread::runDirectCommand (const QString &command) {
 	RK_TRACE (RBACKEND);
 
@@ -1060,7 +1142,8 @@
 	}
 	// running user commands is quite different from all other commands
 	if (ctype & RCommand::User) {
-		// run a user command
+#error restructure, move this, keep comment
+	  // run a user command
 /* Using R_ReplDLLdo1 () is a pain, but it seems to be the only entry point for evaluating a command as if it had been entered on a plain R console (with auto-printing if not invisible, etc.). Esp. since R_Visible is no longer exported in R 2.5.0, as it seems as of today (2007-01-17).
 
 Problems to deal with:
@@ -1081,38 +1164,8 @@
 second 'hist' argument. I didn't look too carefully, but it seems like
 hist == 1 iff R wants a parse-able input.
 */
-
-		R_ReplDLLinit ();		// resets the parse buffer (things might be left over from a previous incomplete parse)
-		bool prev_iteration_was_incomplete = false;
-
-		current_buffer = ccommand.data ();
-
-		repldll_buffer_transfer_finished = false;
-		Rboolean ok = (Rboolean) 1;	// set to false, if there is a jump during the R_ToplevelExec (i.e.. some sort of error)
-
-		repldll_result = 0;
-		while ((ok != FALSE) && ((!repldll_buffer_transfer_finished) || (repldll_result != -1))) {
-			// we always need to iterate until the parse returned an EOF AND we have no more code in the buffer to supply.
-			// However, if this happens right after we last received an INCOMPLETE, this means the parse really was incomplete.
-			// Otherwise, there's simply nothing more to parse.
-			prev_iteration_was_incomplete = (repldll_result == 2);
-			ok = R_ToplevelExec (runUserCommandInternal, 0);
-		}
-		if (ok == FALSE) {
-			if (repldll_last_parse_successful) {
-				error = RThread::OtherError;
-			} else {
-				error = RThread::SyntaxError;
-			}
-		} else {
-			if (prev_iteration_was_incomplete) {
-				error = RThread::Incomplete;
-			} else {
-				error = RThread::NoError;
-			}
-		}
-		repldlldo1_wants_code = false;		// make sure we don't get confused in RReadConsole
 	} else {		// not a user command
+		repl_status.eval_depth++;
 		SEXP parsed = parseCommand (command->command (), &error);
 		if (error == NoError) {
 			SEXP exp;
@@ -1135,6 +1188,7 @@
 			}
 			UNPROTECT (1); // exp
 		}
+		repl_status.eval_depth--;
 	}
 	if (!(ctype & RCommand::Internal)) {
 		if (!locked || killed) processX11Events ();

Modified: branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rembedinternal.h
===================================================================
--- branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rembedinternal.h	2010-10-23 06:50:13 UTC (rev 3139)
+++ branches/2010_10_18_backend_restructuring_branch/rkward/rbackend/rembedinternal.h	2010-10-24 12:40:33 UTC (rev 3140)
@@ -136,7 +136,6 @@
 /** the internal counterpart to pauseOutput () */
 	void waitIfOutputPaused ();
 
-
 /** interrupt processing of the current command. This is much like the user pressing Ctrl+C in a terminal with R. This is probably the only non-portable function in RThread, but I can't see a good way around placing it here, or to make it portable. */
 	void interruptProcessing (bool interrupt);
 
@@ -160,6 +159,8 @@
 Note that you should call initialize only once in a application */
 	int initialize ();
 
+	void enterEventLoop ();
+
 /** clean shutdown of R.
 @param suicidal if true, perform only the most basic shutdown operations */
 	void shutdown (bool suicidal);
@@ -249,6 +250,21 @@
 	bool isKilled () { return killed; };
 
 	QTextCodec *current_locale_codec;
+
+	struct RKReplStatus {
+		QByteArray user_command_buffer;
+		int user_command_transmitted_up_to;
+		bool user_command_completely_transmitted;
+		int user_command_successful_up_to;
+		enum {
+			NoUserCommand,
+			UserCommandTransmitted,
+			UserCommandSyntaxError,
+			UserCommandRunning
+		} user_command_status;
+		int eval_depth;		// Number (depth) of non-user commands currently running. User commands can only run at depth 0
+	};
+	static RKReplStatus repl_status;
 protected:
 /** thread is locked. No new commands will be executed. @see LockType @see lock @see unlock */
 	int locked;
@@ -266,7 +282,7 @@
 protected:
 /** the main loop. See \ref RThread for a more detailed description */
 	void run ();
-private:
+private:  
 /** This is the function in which an RCommand actually gets processed. Basically it passes the command to runCommand () and sends RInterface some events about what is currently happening. */
 	void doCommand (RCommand *command);
 	void notifyCommandDone (RCommand *command);


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the rkward-tracker mailing list