metashell (C++)
==

Metashell (aka /bin/msh) is a word expanding shell and an example of
C++11 coding.  It is a basic experiment in macro expansion and
simplicity.  The language has basically no syntax other than
whitespace separated tokens and is meant to be a compliment to the
Clarity language[1].

EXAMPLE:

bash-3.2$ make
g++ -g --std=c++11 -c main.cpp
g++ -g --std=c++11 -o msh main.o

bash-3.2$ ./msh

Welcome to msh (metashell).

Type words to enter them into the line buffer.
Type 'go' to execute the line buffer in /bin/sh.
Type 'clear' to clear the line buffer
Type 'define   as  .' to define new words.
Type 'lookup  to get a word's definition.
Type 'words' to see all defined words.

[  ]
$ define list as ls
> go
[  ]
$ define list2 x as list x go
> > [  ]
$ list2 .
[ ls  .   ]
$ go
Makefile
README.md
main.cpp
main.o
msh
old
rd
[  ]
$ define list where as ls where go
> > [  ]
$ define status as git 'status' go
[  ]
$ status
[ git 'status'  ]
$ go


And here is the source code:

// 
// main.cpp
//
// Metashell (msh) is macro expanding shell.
//
// Input is collected in to the command buffer.
//
// The command buffer can be executed in /bin/sh using to 'go'
// command.
//
// All input is written to .msh-dribble and it is loaded
// '.msh-dribble' from the current directory at startup.
//
// 'go' commands are not executed on loading of .msh-dribble.
//
// Macros can be defined in the text using the following syntx:
//
//   define   as 
//
// Debug output mode can be toggled by typing 'debug'.
//
//
// Burton Samograd - 2018
// License: AGPL

#include 
#include 
#include 
#include 
#include 

using namespace std;

const string dribbleFile = ".msh-dribble";
bool debug = false;
static list history;

typedef struct tag_Definition {
  list args;
  string definition;
  list words;
} Definition;

void help() {
  cerr << "" << endl;
  cerr << "Welcome to msh (metashell)." << endl;
  cerr << "" << endl;
  cerr << "Type words to enter them into the line buffer." << endl;
  cerr << "Type 'go' to execute the line buffer in /bin/sh." << endl;
  cerr << "Type 'clear' to clear the line buffer" << endl;
  cerr << "Type 'define   as  .' to define new words." << endl;
  cerr << "Type 'lookup  to get a word's definition." << endl;
  cerr << "Type 'words' to see all defined words." << endl;
  cerr << "" << endl;
};

void define(string& word, Definition& d) {
  cout << "define " << word << " ";
  for(auto& arg :  d.args) {
    cout << arg << " ";
  }
  cout << "as " << d.definition << endl;
}

typedef map Dictionary;

string expand(istream& in, string& word, Dictionary& dictionary, bool isStdin) {
  string expansion;
  map params;
  Definition& d = dictionary[word];

  for(auto arg : d.args) {
    in >> params[arg];
    if (params[arg][0] == '"') { // Read quoted string
      while(params[arg][params[arg].length()-1] != '"') {
	string next;
	in >> next;
	params[arg] += " " + next;
      }
    }
  }
  
  for(auto word : d.words) {
    if(params.count(word)) {
      expansion += params[word] += " ";
    } else if(dictionary.count(word)) {
      expansion += expand(in, params[word], dictionary, isStdin) += " ";
    } else {
      if(dictionary.count(word)) {
	expansion += dictionary[word].definition + " ";
      } else {
	expansion += word + " ";
      }
    }
  }
  
  return expansion; // d.definition + " ";
}

void process(istream& in, Dictionary& dictionary, bool isStdin, bool noDribble = true, bool prompt = true, bool noExec = false) {
  ofstream dribble;
  string command;
  bool promptFlag = true;

  if(!noDribble) {
    dribble.open(dribbleFile, ofstream::app);
  }
  
  do {
    string word;
    
    promptFlag && prompt &&
      cerr << endl << "--------------------------------------------------------------------------------" << endl &&
      cerr << "[ " << command << " ]" << endl << "$ ";
    promptFlag = false;
    
    // Read
    in >> word;

    if(word == "") 
      break;

    if(!noDribble || word != "go") {
      dribble << word << " ";
      dribble.flush();
    }

    if(word == "debug") {
      debug = !debug;
      if(debug) {
	cout << "Debugging is enabled." << endl;
      } else {
	cout << "Debugging is disabled." << endl;
      }
      continue;
      promptFlag = true;
    } if(word == "help") {
      help();
      promptFlag = true;
    } else if(word == "go") {
      !noExec && system(command.c_str());
      history.push_back(command);
      command = "";
      promptFlag = true;
    } else if(word == "clear") {
      command = "";
      promptFlag = true;
    } else if(prompt && word == "words") {
      
      for(auto it = dictionary.begin(); it != dictionary.end(); ++it) {
	string word = it->first;
	cout << word << endl;
	//Definition& d = dictionary[word];
  	//define(word, d);
      }
      promptFlag = true;
    } else if(prompt && word == "lookup") {
      string word;
      in >> word;

      Definition& d = dictionary[word];
      define(word, d);
      promptFlag = true;
    } else if(word == "define") {
      // Define
      string word;
      list args;
      list  words;
      string definition;

      in >> word;
      if(!noDribble) {
	dribble << word << " ";
      }

      do {
	string arg;
	cout << "> ";
	in >> arg;
	if(!noDribble) {
	  dribble << arg << " ";
	}

	if(arg == "as") {
	  break;
	}

	args.push_back(arg);
      } while(true);

      while(true) {
	string word2;
	in >> word2;

	words.push_back(word2);
	
	if(!noDribble) {
	  dribble << word2 << " ";
	}
	
	if(word2 == "go") {
	  Definition def;

	  words.pop_back();

	  def.args = args;
	  def.definition = definition;
	  def.words = words;
	  dictionary[word] = def;
	  debug && cout << word << ": " << definition << endl;
	  break;
	}

	if(dictionary.count(word2)) {
	  word2 = dictionary[word2].definition;
	}

	definition += word2 + " ";
	// debug && cout << ".";
      }
      promptFlag = true;
    } else {
      // Expand
      if(dictionary.count(word)) {
	command += expand(in, word, dictionary, isStdin);
      } else {
	command += word + " ";
      }
      //cout << "[ " + command + " ]" << endl;
      promptFlag = true;
    }
    cout.flush();
  } while(!in.eof());
}

int main() {
  map dictionary;

  help();

  ifstream dribble(dribbleFile, ifstream::in);

  process(dribble, dictionary, false, true, false, true);
  process(cin, dictionary, true);
  
  debug && cout << endl;
}

--
		   
> 
--------------------------------------------------------------------------------
[  ]
$ define home as cd go
> 
--------------------------------------------------------------------------------
[  ]
$ cd testing

--------------------------------------------------------------------------------
[ cd  ]
$ 
--------------------------------------------------------------------------------
[ cd testing  ]
$ go
sh: 1: cd: can't cd to testing

--------------------------------------------------------------------------------
[  ]
$ define list as ls go
> 
--------------------------------------------------------------------------------
[  ]
$ list go

--------------------------------------------------------------------------------
[ ls  ]
$ anser  main.cpp  main.o  Makefile  msh	old  rd  README.md  TODO

--------------------------------------------------------------------------------
[  ]
$ list go

--------------------------------------------------------------------------------
[ ls  ]
$ anser  main.cpp  main.o  Makefile  msh	old  rd  README.md  TODO

--------------------------------------------------------------------------------
[  ]
$ define status as blah go
> 
--------------------------------------------------------------------------------
[  ]
$ define status as git 'status' go 
> 
--------------------------------------------------------------------------------
[  ]
$ status go

--------------------------------------------------------------------------------
[ git 'status'  ]
$ On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git restore ..." to discard changes in working directory)
	modified:   main.cpp

no changes added to commit (use "git add" and/or "git commit -a")


References
--
  
[1] Clarity - An English Programming Language

[2] Orignal Github Repository

[3] Local Repository
Burton Samograd 2022