/** * * generates a program to execute a script * * @author http://bumble.sf.net */ #include <iostream> #include <fstream> #include <vector> #include <stack> #include <string> #include <sstream> #include <ctype.h> #include "Machine.h" using namespace std; class Interpret { }; //-- Interpret class //-------------------------------------------- /** this program uses the Machine class to parse a script and * generate c++ code which is capable of executing the script */ int main(int argc, char* argv[]) { string sUsageMessage(""); sUsageMessage.append("ms windows-- \n"); sUsageMessage.append("usage: echo script | Interpret debugflag \n"); sUsageMessage.append("usage: type scriptfile | Interpret debugflag \n"); sUsageMessage.append("unix-- \n"); sUsageMessage.append("usage: echo script | Interpret debugflag \n"); sUsageMessage.append("usage: cat scriptfile | Interpret debugflag \n"); ifstream in; string sMessage(""); bool bDebug = true; if (argc == 1) { bDebug = false; } else if (argc == 2) { if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "-?") == 0)) { cout << sUsageMessage; exit(0); } bDebug = true; } else { bDebug = true; } //-- a machine to parse the script Machine pp; Machine ss; //-- to record descriptions of errors encountered //-- during parsing. stringstream ssErrors(""); stringstream ssWarnings(""); stringstream ssCommandList(""); ssCommandList << "flag, print, clear, read, push, pop, put, get, ++, \n" << "--, newline, indent, clip, state, add 'text', while 'text'\n" << "whilenot 'text', until 'text' 'nottext'\n"; //-- keep track of which line of the source is being parsed int iLineNumber = 0; char cCurrent; //cin.get(cCurrent); pp.readNext(cin); if (bDebug) { cout << "initial state" << pp.printState(); } pp.clear(); while (!cin.eof()) { pp.clear(); pp.readNext(cin); if (pp.workspace() == "\n") { iLineNumber++; } if (bDebug) { cout << pp.printState(); } if (pp.workspace() == ";") { pp.clear(); pp.add("semi-colon|"); pp.push(); } if (pp.workspace() == "!") { pp.clear(); pp.add("not|"); pp.push(); } if (pp.matches("{")) { pp.clear(); pp.add("open-brace|"); pp.push(); } if (pp.matches("}")) { pp.clear(); pp.add("close-brace|"); pp.push(); } //-------------------------------- /* test */ if (pp.workspace() == "/") { /* escape double quotes */ pp.clear(); pp.readUntil(cin, "/", "\\/"); if (!pp.endsWith("/")) { ssErrors << "unterminated test: "; ssErrors << " (line " << iLineNumber << ")" << "\n"; } pp.clip(); pp.replace("\"", "\\\""); pp.put(); pp.clear(); pp.add("(mm.workspace() == \""); pp.get(); pp.add("\")"); pp.put(); pp.clear(); pp.add("test|"); pp.push(); } //-------------------------------- /* begins with test */ if (pp.workspace() == "<") { pp.clear(); pp.readUntil(cin, ">", "\\>"); if (!pp.endsWith(">")) { ssErrors << "unterminated begins with test: "; ssErrors << " (line " << iLineNumber << ")" << "\n"; } pp.clip(); /* escape double quotes */ pp.replace("\"", "\\\""); pp.put(); pp.clear(); pp.add("(mm.beginsWith(\""); pp.get(); pp.add("\"))"); pp.put(); pp.clear(); pp.add("test|"); pp.push(); } if (pp.workspace() == ("[")) { pp.clear(); pp.readUntil(cin, "]", "\\]"); if (!pp.endsWith("]")) { ssErrors << "unterminated class: "; ssErrors << " (line " << iLineNumber << ")" << "\n"; } pp.clip(); pp.put(); pp.clear(); pp.add("class|"); pp.push(); } if (pp.workspace() == "'") { pp.clear(); pp.readUntil(cin, "'", "\\'"); if (!pp.endsWith("'")) { ssErrors << "unterminated quote: "; ssErrors << " (line " << iLineNumber << ")" << "\n"; } pp.clip(); if (pp.workspace() == "") { ssWarnings << "empty quoted text: "; ssWarnings << " (line " << iLineNumber << ")" << "\n"; } pp.replace("\"", "\\\""); pp.put(); pp.clear(); pp.add("quoted-text|"); pp.push(); } if (pp.workspace() == "#") { /* escape double quotes */ pp.clear(); pp.readUntil(cin, "#", "\\#"); if (!pp.endsWith("#")) { ssErrors << "unterminated comment: "; ssErrors << " (line " << iLineNumber << ")" << "\n"; } pp.clip(); if (pp.workspace() == "") { ssWarnings << "empty comment: "; ssWarnings << " (line " << iLineNumber << ")" << "\n"; pp.put(); pp.clear(); } pp.replace("\"", "\\\""); pp.put(); pp.clear(); pp.add("/*"); pp.get(); pp.add("*/"); pp.put(); pp.clear(); pp.add("comment|"); pp.push(); } if (pp.workspace() == "+") { pp.readWhile(cin, "+"); pp.put(); pp.clear(); pp.add("word|"); pp.push(); } if (pp.workspace() == "-") { pp.readWhile(cin, "-"); pp.put(); pp.clear(); pp.add("word|"); pp.push(); } if (pp.workspace() == "\\") { pp.clear(); pp.add("back-slash"); pp.push(); } if (pp.isLetter()) { pp.readWhile(cin, "[:letter:]"); pp.put(); pp.clear(); pp.add("word|"); pp.push(); } if (pp.isSpace()) { pp.readWhile(cin, "[:space:]"); pp.clear(); } bool bReduction = true; while (bReduction) { bReduction = false; pp.pop(); //------------------------------------------- //-- if ((pp.workspace() == "class|")) { pp.clear(); pp.get(); if ((pp.workspace() == ":unicode:")) { pp.clear(); pp.add("mm.isUnicode()"); pp.put(); } else if ((pp.workspace() == ":space:")) { pp.clear(); pp.add("mm.isSpace()"); pp.put(); } else if ((pp.workspace() == ":digit:")) { pp.clear(); pp.add("mm.isDigit()"); pp.put(); } else if ((pp.workspace() == ":letter:")) { pp.clear(); pp.add("mm.isLetter()"); pp.put(); } else { ssErrors << "unrecognized character class"; ssErrors << "[" << pp.workspace() << "]" << endl; ssErrors << "legal classes [:space:], [:digit:], [:letter:]" << endl; } pp.clear(); pp.add("test|"); bReduction = true; } //-- if class if (bDebug) { cout << pp.printState(); } //------------------------------------ pp.pop(); //------------------------------------ /* Errors */ if (pp.workspace() == "quoted-text|close-brace|") { pp.clear(); pp.get(); ssErrors << "incorrect syntax: "; ssErrors << "missing semicolon after '" << pp.workspace() << "' ?"; ssErrors << " (line " << iLineNumber << ")" << "\n"; pp.clear(); pp.get(); pp.add(" #*** missing semi-colon ? ***#"); pp.newline(); pp.add("}"); pp.put(); pp.clear(); pp.add("quoted-text|close-brace|"); } //------------------------------------ /* Errors */ if (pp.workspace() == "word|close-brace|") { pp.clear(); pp.get(); ssErrors << "incorrect syntax: "; ssErrors << "missing semicolon after '" << pp.workspace() << "' ?"; ssErrors << " (line " << iLineNumber << ")" << "\n"; pp.clear(); pp.get(); pp.add(" #*** missing semi-colon ? ***#"); pp.newline(); pp.add("}"); pp.put(); pp.clear(); pp.add("word|close-brace|"); } //------------------------------------ /* Errors */ if (pp.workspace() == "word|word|") { pp.clear(); pp.get(); ssErrors << "incorrect syntax: "; ssErrors << "missing semicolon after '" << pp.workspace() << "' ?"; ssErrors << " (line " << iLineNumber << ")" << "\n"; pp.clear(); pp.get(); pp.add(" #*** missing semi-colon ? ***#"); pp.newline(); pp.incrementTape(); pp.get(); pp.decrementTape(); pp.put(); pp.clear(); pp.add("word|word|"); } if ((pp.workspace() == "comment|command-set|") || (pp.workspace() == "command-set|comment|")) { pp.clear(); pp.get(); pp.newline(); pp.incrementTape(); pp.get(); pp.decrementTape(); pp.put(); pp.clear(); pp.add("command-set|"); pp.push(); bReduction = true; } if ((pp.workspace() == "comment|command|") || (pp.workspace() == "command|comment|")) { pp.clear(); pp.get(); pp.newline(); pp.incrementTape(); pp.get(); pp.decrementTape(); pp.put(); pp.clear(); pp.add("command|"); pp.push(); bReduction = true; } if ((pp.workspace() == "comment|test|") || (pp.workspace() == "test|comment|")) { pp.clear(); pp.get(); pp.add(" "); pp.incrementTape(); pp.get(); pp.decrementTape(); pp.put(); pp.clear(); pp.add("test|"); pp.push(); bReduction = true; } if (pp.workspace() == "not|test|") { pp.clear(); pp.add("!"); pp.incrementTape(); pp.get(); pp.decrementTape(); pp.put(); pp.clear(); pp.add("test|"); pp.push(); bReduction = true; } if ((pp.workspace() == "command-set|command|")) { pp.clear(); pp.get(); pp.newline(); pp.incrementTape(); pp.get(); pp.decrementTape(); pp.put(); pp.clear(); pp.add("command-set|"); pp.push(); bReduction = true; } if ((pp.workspace() == "command|command|")) { pp.clear(); pp.get(); pp.newline(); pp.incrementTape(); pp.get(); pp.decrementTape(); pp.put(); pp.clear(); pp.add("command-set|"); pp.push(); bReduction = true; } //------------------------------------------- /* allow multiple tests for one block, for example /noun|verb|/ /article|noun|verb/ { clear; add 'sentence'; push; } */ if ((pp.workspace() == "test|test|")) { pp.clear(); pp.get(); pp.add(" ||"); pp.newline(); pp.add(" "); pp.incrementTape(); pp.get(); pp.decrementTape(); pp.put(); pp.clear(); pp.add("test|"); pp.push(); } if ((pp.workspace() == "word|comment|")) { pp.clear(); ssWarnings << "badly placed comment: "; ssWarnings << " (line " << iLineNumber << ")" << "\n"; pp.add("word|"); pp.push(); bReduction = true; } //--if word, comment if ((pp.workspace() == "word|semi-colon|")) { pp.clear(); pp.get(); if ((pp.workspace() == "print")) { pp.clear(); pp.add("mm.print();"); } else if ((pp.workspace() == "flag")) { pp.clear(); pp.add("mm.setFlag();"); } else if ((pp.workspace() == "clear")) { pp.clear(); pp.add("mm.clear();"); } else if ((pp.workspace() == "read")) { pp.clear(); /* if the script is in a stack reduction loop, it shouldnt * read another character from standard in */ pp.add("if (!mm.stackFlag())"); pp.newline(); pp.add("{"); pp.newline(); pp.add(" mm.readNext(cin);"); pp.newline(); pp.add("}"); } else if ((pp.workspace() == "push")) { pp.clear(); pp.add("mm.push();"); } else if ((pp.workspace() == "pop")) { pp.clear(); pp.add("mm.pop();"); } else if ((pp.workspace() == "put")) { pp.clear(); pp.add("mm.put();"); } else if ((pp.workspace() == "get")) { pp.clear(); pp.add("mm.get();"); } else if ((pp.workspace() == "++")) { pp.clear(); pp.add("mm.incrementTape();"); } else if ((pp.workspace() == "--")) { pp.clear(); pp.add("mm.decrementTape();"); } else if ((pp.workspace() == "newline")) { pp.clear(); pp.add("mm.newline();"); } else if ((pp.workspace() == "indent")) { pp.clear(); pp.add("mm.indent();"); } else if ((pp.workspace() == "clip")) { pp.clear(); pp.add("mm.clip();"); } else if ((pp.workspace() == "state")) { pp.clear(); pp.add("cout << mm.printState();"); } else if ((pp.workspace() == "add")) { pp.add(";"); pp.add(" # command requires argument #"); ssErrors << "incorrect syntax: "; ssErrors << "the 'add' command requires an argument."; ssErrors << " (line " << iLineNumber << ")" << "\n"; } else if ((pp.workspace() == "while")) { pp.add(";"); pp.add(" # command requires argument #"); ssErrors << "incorrect syntax: "; ssErrors << "'while' command requires an argument."; ssErrors << " (line " << iLineNumber << ")" << "\n"; } else if ((pp.workspace() == "whilenot")) { pp.add(";"); pp.add(" # command requires argument #"); ssErrors << "incorrect syntax: "; ssErrors << "the 'whilenot' command requires an argument."; ssErrors << " (line " << iLineNumber << ")" << "\n"; } else if ((pp.workspace() == "until")) { pp.add(";"); pp.add(" # command requires argument #"); ssErrors << "incorrect syntax: "; ssErrors << "the 'until' command requires an argument."; ssErrors << " (line " << iLineNumber << ")" << "\n"; } else { ssErrors << "unrecognized command: '" << pp.workspace(); ssErrors << "' (line " << iLineNumber << ") \n"; ssErrors << "legal commands: \n" << ssCommandList.str(); pp.add(";"); pp.add(" #-- unknown command --#"); } pp.put(); pp.clear(); pp.add("command|"); pp.push(); bReduction = true; } //--if word,semi-colon pp.pop(); if ((pp.workspace() == "word|quoted-text|semi-colon|")) { pp.clear(); pp.get(); if ((pp.workspace() == "add")) { pp.clear(); pp.add("mm.add(\""); pp.incrementTape(); pp.get(); pp.decrementTape(); pp.add("\");"); pp.put(); } else if ((pp.workspace() == "until")) { pp.clear(); pp.add("mm.readUntil(cin, \""); pp.incrementTape(); pp.get(); pp.decrementTape(); pp.add("\");"); pp.put(); } else if ((pp.workspace() == "while")) { pp.clear(); pp.add("mm.readWhile(cin, \""); pp.incrementTape(); pp.get(); pp.decrementTape(); pp.add("\");"); pp.put(); } else if ((pp.workspace() == "whilenot")) { pp.clear(); pp.add("mm.readWhileNot(cin, \""); pp.incrementTape(); pp.get(); pp.decrementTape(); pp.add("\");"); pp.put(); } else { ssErrors << "unrecognized command: '" << pp.workspace(); ssErrors << "' (line " << iLineNumber << ")" << "\n"; pp.add(";"); pp.add(" #-- unknown command --#"); } pp.clear(); pp.add("command|"); pp.push(); bReduction = true; } //--if word, text, semi-colon pp.pop(); /* two parameters */ if ((pp.workspace() == "word|quoted-text|quoted-text|semi-colon|")) { pp.clear(); pp.get(); if ((pp.workspace() == "until")) { pp.clear(); pp.add("mm.readUntil(cin, \""); pp.incrementTape(); pp.get(); pp.add("\", \""); pp.incrementTape(); pp.get(); pp.add("\");"); pp.decrementTape(); pp.decrementTape(); pp.put(); } else if ((pp.workspace() == "replace")) { pp.clear(); pp.add("mm.replace(\""); pp.incrementTape(); pp.get(); pp.add("\", \""); pp.incrementTape(); pp.get(); pp.add("\");"); pp.decrementTape(); pp.decrementTape(); pp.put(); } else { ssErrors << "unrecognized command: " << pp.workspace(); ssErrors << " (line " << iLineNumber << ") \n"; ssErrors << "legal commands: \n"; ssErrors << ssCommandList.str(); pp.add(";"); pp.add(" # unknown command #"); } pp.clear(); pp.add("command|"); pp.push(); bReduction = true; } //--if word, text, text, semi-colon if ((pp.workspace() == "test|open-brace|command|close-brace|") || (pp.workspace() == "test|open-brace|command-set|close-brace|")) { //-- indent a code block pp.clear(); pp.incrementTape(); pp.incrementTape(); pp.get(); pp.indent(); pp.put(); pp.decrementTape(); pp.decrementTape(); pp.clear(); //-- put the if around the tests pp.add("if ("); pp.get(); pp.add(")"); pp.newline(); //--pp.add("bReduction = true;"); //--pp.newline(); pp.add("{"); pp.newline(); pp.incrementTape(); pp.incrementTape(); pp.get(); pp.decrementTape(); pp.decrementTape(); pp.newline(); pp.add("}"); pp.newline(); pp.put(); pp.clear(); pp.add("command|"); pp.push(); bReduction = true; } //--if test, block pp.push(); pp.push(); pp.push(); pp.push(); if (bDebug) { cout << pp.printState(); } } //-- while a reduction } //-- for each letter if (pp.stacksize() != 1) { pp.pop(); if ((pp.workspace() == "quoted-text|") || (pp.workspace() == "word|")) { pp.clear(); pp.get(); ssErrors << "incorrect syntax: "; ssErrors << "missing semicolon after '" << pp.workspace() << "' ?"; ssErrors << " (line " << iLineNumber << ")" << "\n"; pp.clear(); } cout << "*** error parsing script (no start symbol) ***\n\n"; if (ssErrors.str().length() > 0) { cout << "--errors--" << endl; cout << ssErrors.str(); } if (ssWarnings.str().length() > 0) { cout << "--warnings--" << endl; cout << ssWarnings.str(); } cout << "*** final parse state ***" << endl; cout << pp.printState(); exit(-1); } pp.clear(); pp.pop(); if ((pp.workspace() != "command|") && (pp.workspace() != "command-set|")) { cout << "*** error parsing script (neither command nor list of commands) ***\n\n"; if (ssErrors.str().length() > 0) { cout << "--errors--" << endl; cout << ssErrors.str(); } if (ssWarnings.str().length() > 0) { cout << "--warnings--" << endl; cout << ssWarnings.str(); } cout << "*** final parse state ***" << endl; cout << pp.printState(); exit(-1); } if (ssErrors.str().length() > 0) { cout << "*** error in script \n\n"; cout << ssErrors.str(); if (ssWarnings.str().length() > 0) { cout << "Warnings:" << endl; cout << ssWarnings.str(); } pp.clear(); pp.newline(); pp.get(); pp.print(); //cout << "*** final parse state ***" << endl; //cout << pp.printState(); exit(-1); } cout << "//-- script successfully parsed." << endl; pp.clear(); pp.get(); pp.indent(); pp.indent(); pp.indent(); pp.indent(); pp.indent(); pp.put(); pp.clear(); //-- add the skeleton code // pp.add("#include <iostream>"); pp.newline(); pp.add("#include <fstream>"); pp.newline(); pp.add("#include <vector>"); pp.newline(); pp.add("#include <stack>"); pp.newline(); pp.add("#include <string>"); pp.newline(); pp.add("#include <sstream>"); pp.newline(); pp.add("#include <ctype.h>"); pp.newline(); pp.add("#include \"Machine.h\""); pp.newline(); pp.newline(); pp.newline(); pp.newline(); pp.newline(); pp.add(" int main(int argc, char* argv[])"); pp.newline(); pp.add(" {"); pp.newline(); pp.add(" string sbUsageMessage(\"\");"); pp.newline(); pp.add(" sbUsageMessage.append(\"usage: \");"); pp.newline(); pp.add(" sbUsageMessage.append(\"\\n\");"); pp.newline(); pp.add(" if (argc == 1)"); pp.newline(); pp.add(" {"); pp.newline(); pp.add(" }"); pp.newline(); pp.add(" else"); pp.newline(); pp.add(" {"); pp.newline(); pp.add(" }"); pp.newline(); pp.newline(); pp.add(" Machine mm;"); pp.newline(); pp.add(" string ssErrors(\"\");"); pp.newline(); pp.add(" string sbPartial(\"\");"); pp.newline(); pp.add(" bool bReduction = false;"); pp.newline(); pp.add(" bool bDebug = false;"); pp.newline(); pp.add(" char cCurrent;"); pp.newline(); pp.newline(); pp.newline(); pp.add(" mm.readNext(cin);"); pp.newline(); pp.add(" mm.clear();"); pp.newline(); pp.add(" while (!cin.eof())"); pp.newline(); pp.add(" {"); pp.newline(); //pp.add(" mm.readNext(cin);"); pp.newline(); pp.add(" if (bDebug)"); pp.newline(); pp.add(" {"); pp.newline(); pp.add(" cout << \"character:\" << cCurrent << endl; "); pp.newline(); pp.add(" cout << mm.printState();"); pp.newline(); pp.add(" }"); pp.newline(); pp.add(" mm.setFlag();"); pp.newline(); pp.add(" while (mm.stackFlag())"); pp.newline(); pp.add(" {"); pp.newline(); pp.add(" mm.resetFlag();"); pp.newline(); //------------------------------------- pp.get(); pp.newline(); pp.add(" } //-- while reduction"); pp.newline(); pp.add(" } //-- for each letter"); pp.newline(); pp.add(" } //-- main()"); pp.newline(); pp.print(); if (ssWarnings.str().length() > 0) { cout << "//--Warnings generated by parser: \n"; cout << "/* \n" << ssWarnings.str() << "\n*/" << endl; } if (bDebug) { cout << pp.printState(); } } //-- main()