Skip to content

Commit

Permalink
Resolves #2: Command line arguments, inoutstrings in particular, sh…
Browse files Browse the repository at this point in the history
…ould now be respected. Also did superficial fixes to satisfy the linter.
  • Loading branch information
rljacobson committed Oct 16, 2019
1 parent a688f4a commit 7476031
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 65 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ then read the section "Preparing your environment" above.

Option| Description
----------------------------|---------------------------
` --mainloop arg (=1)` |Boolean. Whether or not to use the kernel's Main Loop which keeps track of session history with In[#] and Out[#] variables. Defaults to true.
` --mainloop arg (=1)` |Boolean. Whether or not to use the kernel's Main Loop which keeps track of session history with `In[#]` and `Out[#]` variables. Defaults to true.
`--prompt arg ` |String. The prompt presented to the user for input. When inoutstrings is true, this is typically the empty string.
`--inoutstrings arg (=1)` |Boolean. Whether or not to print the `In[#]:=` and `Out[#]=` strings. When mainloop is false this option does nothing. Defaults to true.
`--linkname arg` |String. The call string to set up the link. The default works on *nix systems on which math is in the path and runnable. Defaults to `"math -wstp"`.
Expand Down Expand Up @@ -116,4 +116,4 @@ This software includes linenoise ([http://github.com/antirez/linenoise](http://g

This software includes FindMathematica ([https://github.com/sakra/FindMathematica](https://github.com/sakra/FindMathematica)), a CMake module by Sascha Kratky that tries to find a Mathematica installation and provides CMake functions for Mathematica's C/C++ interface. FindMathematica is licensed under the MIT license. See LICENSE-FindMathematica.txt for more information.

This software includes popl ([https://github.com/badaix/popl](https://github.com/badaix/popl)), a c++ command line arguments parser by Johannes Pohl licensed under the MIT license. See LICENSE-popl.txt for more information.
This software includes popl ([https://github.com/badaix/popl](https://github.com/badaix/popl)), a c++ command line arguments parser by Johannes Pohl licensed under the MIT license. See LICENSE-popl.txt for more information.
30 changes: 17 additions & 13 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#pragma clang diagnostic push
#pragma ide diagnostic ignored "hicpp-avoid-c-arrays"
//
// MathLine
//
Expand All @@ -13,12 +15,12 @@
#define QUIT_WITH_SUCCESS 1
#define QUIT_WITH_ERROR 2

//Why is this not part of std::string?
char *copyDataFromString(const std::string str){
char * newstring = new char[str.size() + 1];
std::copy(str.begin(), str.end(), newstring);
newstring[str.size()] = '\0';
return newstring;
/// Copy `std::string` into an owning C-style string.
char *copyDataFromString(const std::string &str){
char *new_string = new char[str.size() + 1];
std::copy(str.begin(), str.end(), new_string);
new_string[str.size()] = '\0';
return new_string;
}

bool check_and_exit = false;
Expand Down Expand Up @@ -50,16 +52,16 @@ int ParseProgramOptions(MLBridge &bridge, int argc, const char * argv[]){
// Parse the options.
try{
op.parse(argc, argv);
}catch (std::invalid_argument e){
}catch (std::invalid_argument &e){
std::cout << "Error: " << e.what() << ".\n";
std::cout << op << std::endl;
return QUIT_WITH_ERROR;
};

//Check for unknown options.
if( op.unknownOptions().size() > 0) {
for (size_t n = 0; n < op.unknownOptions().size(); ++n)
std::cout << "Unknown option: " << op.unknownOptions()[n] << "\n";
if( !op.unknownOptions().empty()) {
for(const auto &n : op.unknownOptions())
std::cout << "Unknown option: " << n << "\n";
std::cout << op << std::endl;
return QUIT_WITH_ERROR;
}
Expand Down Expand Up @@ -114,20 +116,20 @@ int main(int argc, const char * argv[]) {

//Parse the command line arguments.
int parseFailed = ParseProgramOptions(bridge, argc, argv);
if (parseFailed) {
if (CONTINUE != parseFailed) {
// An unknown argument was supplied, so we exit.
return parseFailed;
}

//Attempt to establish the MathLink connection using the options we've set.
try{
bridge.Connect();
} catch(MLBridgeException e){
} catch(MLBridgeException &e){
std::cerr << e.ToString() << "\n";
std::cerr << "Could not connect to Mathematica. Check that " << bridge.argv[3] << " works from a command line." << std::endl;
return 1;
}
if(bridge.isConnected()){
if(bridge.IsConnected()){
//Let's print the kernel version.
std::cout << "Mathematica " << bridge.GetKernelVersion() << "\n" << std::endl;
if( check_and_exit ){
Expand All @@ -145,3 +147,5 @@ int main(int argc, const char * argv[]) {

return 0;
}

#pragma clang diagnostic pop
78 changes: 46 additions & 32 deletions src/mlbridge.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#pragma clang diagnostic push
#pragma ide diagnostic ignored "hicpp-avoid-c-arrays"
//
// mlbridge.cpp
// MathLinkBridge
Expand All @@ -10,6 +12,7 @@
// a macro. See config.h for details.

#include <iostream>
#include <utility>
#include <wstp.h>

// TODO: Handle signal interrupts correctly.
Expand All @@ -21,13 +24,15 @@

//Used for printf() debugging.
bool debug = false;
void DebugPrint(std::string msg){
void DebugPrint(const std::string &msg){
//std::ostream &cout = *pcout;
if(debug) std::cout << msg << "\n";
}


MLBridgeException::MLBridgeException(std::string error, int errorCode): errorMsg(error), errorCode(errorCode){
MLBridgeException::MLBridgeException(std::string error, int errorCode):
errorMsg(std::move(error)),
errorCode(errorCode){
//Pass.
}

Expand Down Expand Up @@ -62,13 +67,13 @@ void MLBridge::Connect(){
connected = false;

//If no parameters are specified and this has no default parameters, bail.
if(argc==0 || argv==NULL){
if(argc==0 || argv==nullptr){
throw MLBridgeException("No " MMANAME " parameters specified for the connection.");
}

//Initialize the link to Mathematica.
environment = MMAInitialize(NULL);
if(environment == NULL){
environment = MMAInitialize(nullptr);
if(environment == nullptr){
//Failed to initialize the link.
throw MLBridgeException("Cannot initialize " MMANAME ".");
}
Expand All @@ -78,8 +83,8 @@ void MLBridge::Connect(){
DebugPrint("Calling WSOpenArgcArgv...");
link = MMAOpenArgcArgv( environment, argc, (char **)argv, &error);
DebugPrint("WSOpenArgcArgv returned: " + std::to_string(error));
if (link == NULL || error != MMAEOK) {
DebugPrint("Link is null or error.");
if (link == nullptr || error != MMAEOK) {
DebugPrint("Link is nullptr or error.");
//The link failed to open.
connected = false;
MMADeinitialize(environment);
Expand Down Expand Up @@ -133,8 +138,15 @@ void MLBridge::InitializeKernel() {
}

std::string MLBridge::ReadInput(){
std::string inputString;
std::string promptToUser = prompt + kernelPrompt;
std::string input;
std::string promptToUser;

if(showInOutStrings){
promptToUser = prompt + kernelPrompt;
} else{
promptToUser = prompt;
}

if(continueInput){
promptToUser.replace(0, promptToUser.length()-1, promptToUser.length(), ' ');
}
Expand All @@ -148,7 +160,7 @@ std::string MLBridge::ReadInput(){

cout << promptToUser;

std::getline(cin, inputString);
std::getline(cin, input);
//Check the status of cin.
if (!cin.good()){
//A ctrl+c event inside of getline introduces an internal error in cin. We attempt to clear the error.
Expand All @@ -161,12 +173,12 @@ std::string MLBridge::ReadInput(){
char *line;
line = linenoise(promptToUser.data());
linenoiseHistoryAdd(line);
inputString = std::string(line);
input = std::string(line);
free(line);
}

kernelPrompt = "";
return inputString;
return input;
}

void MLBridge::SetMaxHistory(int max){
Expand All @@ -177,26 +189,26 @@ void MLBridge::SetMaxHistory(int max){

void MLBridge::REPL(){
std::ostream &cout = *pcout;
std::string inputString;
std::string input;

//For convenience we wrap everything in a try-block. However, some errors are recoverable. Though we do not do this, one could attempt to clear the error and restart the REPL.
try {
while(true){
inputString = ReadInput();
if( inputString == "Exit" || inputString == "Exit[]" || inputString == "Quit" ) break;
Evaluate(inputString);
input = ReadInput();
if( input == "Exit" || input == "Exit[]" || input == "Quit" ) break;
Evaluate(input);
//Read and act on response from the kernel.
ProcessKernelResponse();
}
} catch (MLBridgeException e) {
} catch (MLBridgeException &e) {
cout << e.ToString() << std::endl;
}
}

std::string MLBridge::GetUTF8String(GetFunctionType func){
//func defaults to GetString.
//MLGetUTF8String does NOT null terminate the string.
const unsigned char *stringBuffer = NULL;
//MLGetUTF8String does NOT nullptr terminate the string.
const unsigned char *stringBuffer = nullptr;
int bytes = 0;
int characters;
int success = 0;
Expand Down Expand Up @@ -256,7 +268,7 @@ void MLBridge::ErrorCheck(){
int errorCode;
std::string error;

if(link == NULL){
if(link == nullptr){
throw MLBridgeException("The " MMANAME " connection has been severed.", MMAEDEAD);
}
errorCode = MMAError(link);
Expand All @@ -276,13 +288,13 @@ void MLBridge::ErrorCheck(){
}
}

void MLBridge::Evaluate(std::string inputString){
//We record the inputString so that we can reference it later, for example, in the event of a syntax error.
void MLBridge::Evaluate(const std::string &input){
//We record the input so that we can reference it later, for example, in the event of a syntax error.
if(continueInput){
inputString = this->inputString.append(inputString);
inputString.append(input);
continueInput = false;
} else{
this->inputString = inputString;
inputString = input;
}

/*
Expand Down Expand Up @@ -313,19 +325,19 @@ void MLBridge::Evaluate(std::string inputString){
running = true;
}

void MLBridge::EvaluateWithoutMainLoop(std::string inputString, bool eatReturnPacket){
void MLBridge::EvaluateWithoutMainLoop(const std::string &input, bool eatReturnPacket){
// This function always circumvents the kernel's Main Loop. Use it for setting $PrePrint for example.

if(!isConnected()){
if(!IsConnected()){
throw MLBridgeException("Tried to evaluate without being connected to a kernel.");
}

//We record the inputString so that we can reference it later, for example, in the event of a syntax error.
//We record the input so that we can reference it later, for example, in the event of a syntax error.
if(continueInput){
inputString = this->inputString.append(inputString);
inputString.append(input);
continueInput = false;
} else{
this->inputString = inputString;
inputString = input;
}

//Bypass the kernel's Main Loop.
Expand All @@ -345,15 +357,15 @@ void MLBridge::EvaluateWithoutMainLoop(std::string inputString, bool eatReturnPa
}
}

void MLBridge::SetPrePrint(std::string preprintfunction){
void MLBridge::SetPrePrint(const std::string &preprintfunction){
EvaluateWithoutMainLoop("$PrePrint = " + preprintfunction);
}

std::string MLBridge::GetKernelVersion() {
return GetEvaluated("$Version");
}

std::string MLBridge::GetEvaluated(std::string expression){
std::string MLBridge::GetEvaluated(const std::string &expression){

EvaluateWithoutMainLoop(expression, false);
//EvaluateWithoutMainLoop sets running to true, but we get the return string ourselves, so we leaving running = false;
Expand Down Expand Up @@ -510,7 +522,7 @@ bool MLBridge::ReceivedDisplayEndPacket(){

//If we want to include our own postscript "post-amble", this is where it would go.
images.push(*image);
image = NULL;
image = nullptr;
makeNewImage = true;

//It is unclear if we still return false when useMainLoop is false.
Expand Down Expand Up @@ -750,3 +762,5 @@ void MLBridge::ProcessKernelResponse() {

return;
}

#pragma clang diagnostic pop
Loading

0 comments on commit 7476031

Please sign in to comment.