-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #44 from alexdovzhanyn/repl-code-execution
REPL code execution
- Loading branch information
Showing
9 changed files
with
271 additions
and
122 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,129 +1,104 @@ | ||
#include "CLI.hpp" | ||
#include <iostream> | ||
#include <algorithm> | ||
#include <readline/readline.h> | ||
#include <readline/history.h> | ||
#include "../../version.h" | ||
#include "../compiler/Compiler.hpp" | ||
#include "REPL.hpp" | ||
|
||
using namespace Theta; | ||
using namespace std; | ||
|
||
namespace Theta { | ||
class CLI { | ||
public: | ||
static void parseCommand(int argc, char* argv[]) { | ||
bool isEmitTokens = false; | ||
bool isEmitAST = false; | ||
bool isEmitWAT = false; | ||
string sourceFile; | ||
string outFile; | ||
|
||
if (argc == 1) { | ||
repl(); | ||
} else if (argc == 2) { | ||
string arg1 = argv[1]; | ||
|
||
if (arg1 == "--version") return printLanguageVersion(); | ||
if (arg1 == "--help") return printUsageInstructions(); | ||
|
||
sourceFile = argv[1]; | ||
} else { | ||
int i = 1; | ||
|
||
while (i < argc) { | ||
string arg = argv[i]; | ||
|
||
if (arg == "-o") { | ||
outFile = argv[i + 1]; | ||
i++; | ||
} | ||
else if (arg == "--emitTokens") isEmitTokens = true; | ||
else if (arg == "--emitAST") isEmitAST = true; | ||
else if (arg == "--emitWAT") isEmitWAT = true; | ||
else if (i == argc - 1) sourceFile = arg; | ||
else validateOption(arg); | ||
|
||
i++; | ||
} | ||
} | ||
|
||
if (sourceFile == "") return; | ||
|
||
if (outFile == "") { | ||
bool reachedDelimiter = false; | ||
for (int i = 0; !reachedDelimiter; i++) { | ||
outFile += sourceFile[i]; | ||
|
||
if (sourceFile[i + 1] == '.') reachedDelimiter = true; | ||
} | ||
|
||
outFile += ".wasm"; | ||
} | ||
|
||
Theta::Compiler::getInstance().compile(sourceFile, outFile, isEmitTokens, isEmitAST, isEmitWAT); | ||
} | ||
void CLI::parseCommand(int argc, char* argv[]) { | ||
bool isEmitTokens = false; | ||
bool isEmitAST = false; | ||
bool isEmitWAT = false; | ||
string sourceFile; | ||
string outFile; | ||
|
||
static string makeLink(string url, string text = "") { | ||
return "\x1B]8;;" + url + "\x1B\\" + (text != "" ? text : url) + "\x1B]8;;\x1B\\"; | ||
} | ||
if (argc == 1) { | ||
REPL repl = REPL(); | ||
repl.readInput(); | ||
} else if (argc == 2) { | ||
string arg1 = argv[1]; | ||
|
||
private: | ||
static void printUsageInstructions() { | ||
cout << "Theta Language Compiler CLI" << endl; | ||
cout << "============================" << endl; | ||
cout << endl; | ||
cout << "Usage:" << endl; | ||
cout << " theta [options] <source_file>" << endl; | ||
cout << endl; | ||
cout << "Options:" << endl; | ||
cout << " -o <output_file> Specify the output file name." << endl; | ||
cout << " --emitTokens Emit the tokenized representation of the source file produced by the lexer." << endl; | ||
cout << " --emitAST Emit the Abstract Syntax Tree (AST) representation produced by the parser." << endl; | ||
cout << " --emitWAT Emit the WebAssembly Text format (WAT) representation produced." << endl; | ||
cout << " --help Display this help message and exit." << endl; | ||
cout << " --version Display the currently installed Theta language version and exit." << endl; | ||
} | ||
if (arg1 == "--version") return printLanguageVersion(); | ||
if (arg1 == "--help") return printUsageInstructions(); | ||
|
||
static void printLanguageVersion() { | ||
cout << "Theta v" << VERSION_MAJOR << '.' << VERSION_MINOR << '.' << VERSION_PATCH << endl; | ||
} | ||
sourceFile = argv[1]; | ||
} else { | ||
int i = 1; | ||
|
||
static bool validateOption(string option) { | ||
vector<string> validOptions = { | ||
"--version", | ||
"--help", | ||
"--emitTokens", | ||
"--emitAST", | ||
"--emitWAT", | ||
"-o" | ||
}; | ||
|
||
if (find(validOptions.begin(), validOptions.end(), option) == validOptions.end()) { | ||
cout << "Invalid option: " + option << endl; | ||
return false; | ||
} | ||
|
||
return true; | ||
while (i < argc) { | ||
string arg = argv[i]; | ||
|
||
if (arg == "-o") { | ||
outFile = argv[i + 1]; | ||
i++; | ||
} | ||
else if (arg == "--emitTokens") isEmitTokens = true; | ||
else if (arg == "--emitAST") isEmitAST = true; | ||
else if (arg == "--emitWAT") isEmitWAT = true; | ||
else if (i == argc - 1) sourceFile = arg; | ||
else validateOption(arg); | ||
|
||
static void repl() { | ||
cout << "Interactive Theta" << endl; | ||
printLanguageVersion(); | ||
cout << "Report issues at " + makeLink("https://www.github.com/alexdovzhanyn/ThetaLang/issues") << endl; | ||
cout << "CTRL+D to exit" << endl << endl; | ||
i++; | ||
} | ||
} | ||
|
||
char* input; | ||
if (sourceFile == "") return; | ||
|
||
while ((input = readline("ith $> ")) != nullptr) { | ||
if (*input) add_history(input); | ||
if (outFile == "") { | ||
bool reachedDelimiter = false; | ||
for (int i = 0; !reachedDelimiter; i++) { | ||
outFile += sourceFile[i]; | ||
|
||
// TODO: Change this to get execution result once we get an interpreter built out | ||
Theta::Compiler::getInstance().compileDirect(input); | ||
Theta::Compiler::getInstance().clearExceptions(); | ||
if (sourceFile[i + 1] == '.') reachedDelimiter = true; | ||
} | ||
|
||
free(input); | ||
} | ||
outFile += ".wasm"; | ||
} | ||
|
||
cout << endl << endl << "Exiting ITH..." << endl; | ||
} | ||
Theta::Compiler::getInstance().compile(sourceFile, outFile, isEmitTokens, isEmitAST, isEmitWAT); | ||
} | ||
|
||
string CLI::makeLink(string url, string text) { | ||
return "\x1B]8;;" + url + "\x1B\\" + (text != "" ? text : url) + "\x1B]8;;\x1B\\"; | ||
} | ||
|
||
void CLI::printLanguageVersion() { | ||
cout << "Theta v" << VERSION_MAJOR << '.' << VERSION_MINOR << '.' << VERSION_PATCH << endl; | ||
} | ||
|
||
void CLI::printUsageInstructions() { | ||
cout << "Theta Language Compiler CLI" << endl; | ||
cout << "============================" << endl; | ||
cout << endl; | ||
cout << "Usage:" << endl; | ||
cout << " theta [options] <source_file>" << endl; | ||
cout << endl; | ||
cout << "Options:" << endl; | ||
cout << " -o <output_file> Specify the output file name." << endl; | ||
cout << " --emitTokens Emit the tokenized representation of the source file produced by the lexer." << endl; | ||
cout << " --emitAST Emit the Abstract Syntax Tree (AST) representation produced by the parser." << endl; | ||
cout << " --emitWAT Emit the WebAssembly Text format (WAT) representation produced." << endl; | ||
cout << " --help Display this help message and exit." << endl; | ||
cout << " --version Display the currently installed Theta language version and exit." << endl; | ||
} | ||
|
||
bool CLI::validateOption(string option) { | ||
vector<string> validOptions = { | ||
"--version", | ||
"--help", | ||
"--emitTokens", | ||
"--emitAST", | ||
"--emitWAT", | ||
"-o" | ||
}; | ||
|
||
if (find(validOptions.begin(), validOptions.end(), option) == validOptions.end()) { | ||
cout << "Invalid option: " + option << endl; | ||
return false; | ||
} | ||
|
||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#pragma once | ||
|
||
#include <string> | ||
|
||
using namespace std; | ||
|
||
namespace Theta { | ||
class CLI { | ||
public: | ||
static void parseCommand(int argc, char* argv[]); | ||
|
||
static string makeLink(string url, string text = ""); | ||
|
||
static void printLanguageVersion(); | ||
|
||
private: | ||
static void printUsageInstructions(); | ||
|
||
static bool validateOption(string option); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
#include "REPL.hpp" | ||
#include <readline/readline.h> | ||
#include <readline/history.h> | ||
#include "compiler/Compiler.hpp" | ||
#include "CLI.hpp" | ||
#include "runtime/Runtime.hpp" | ||
#include <map> | ||
|
||
using namespace Theta; | ||
using namespace std; | ||
|
||
stack<char> REPL::delimeterStack; | ||
int REPL::lineNumber = 0; | ||
|
||
REPL::REPL() { | ||
cout << "Interactive Theta" << endl; | ||
CLI::printLanguageVersion(); | ||
cout << "Report issues at " + CLI::makeLink("https://www.github.com/alexdovzhanyn/ThetaLang/issues") << endl; | ||
cout << "CTRL+D to exit" << endl << endl; | ||
} | ||
|
||
REPL::~REPL() { | ||
cout << endl << endl << "Exiting ITH..." << endl; | ||
} | ||
|
||
void REPL::readInput() { | ||
char* input; | ||
string accumulatedInput; | ||
|
||
rl_pre_input_hook = preInputHook; | ||
|
||
while ((input = readline(getPrompt().c_str())) != nullptr) { | ||
accumulatedInput += string(input); | ||
lineNumber++; | ||
|
||
// For multiline input. We keep a stack of the matching braces so we know once a statement is complete. | ||
for (char &c : string(input)) { | ||
if (c == '{' || c == '(' || c == '[') { | ||
delimeterStack.push(c); | ||
} else if (isMatchingDelimeter(c, delimeterStack)) { | ||
delimeterStack.pop(); | ||
} | ||
} | ||
|
||
// If all of the delimeters weren't resolved, take in more user input | ||
if (!delimeterStack.empty()) { | ||
// Only add the newline in the case where we expect more input. Otherwise it's pointless | ||
accumulatedInput += "\n"; | ||
free(input); | ||
|
||
string indents = ""; | ||
for (int i = 0; i < delimeterStack.size(); ++i) { | ||
indents += " "; | ||
} | ||
|
||
continue; | ||
} | ||
|
||
if (!accumulatedInput.empty()) execute(accumulatedInput); | ||
|
||
free(input); | ||
accumulatedInput.clear(); | ||
} | ||
} | ||
|
||
void REPL::execute(string source) { | ||
add_history(source.c_str()); | ||
|
||
vector<char> wasm = Compiler::getInstance().compileDirect(source); | ||
|
||
if (wasm.size() > 0) { | ||
ExecutionContext context = Runtime::getInstance().execute(wasm, "main0"); | ||
|
||
cout << "\x1B[33m-----> " << context.stringifiedResult() << "\x1B[0m" << endl << endl; | ||
} | ||
|
||
Compiler::getInstance().clearExceptions(); | ||
} | ||
|
||
void REPL::prefillIndentation() { | ||
string indents(delimeterStack.size() * 2, ' '); | ||
|
||
rl_insert_text(indents.c_str()); | ||
rl_point = rl_end; | ||
rl_redisplay(); | ||
} | ||
|
||
string REPL::getPrompt() { | ||
string promptStyleSet = "\x1B[32m"; | ||
string promptStyleClear = "\x1B[0m"; | ||
|
||
string prompt = delimeterStack.size() == 0 ? "ith" : "..."; | ||
string line = "(" + to_string(lineNumber) + ")"; | ||
|
||
return promptStyleSet + prompt + line + " $> " + promptStyleClear; | ||
} | ||
|
||
bool REPL::isMatchingDelimeter(char &c, stack<char> delimeterStack) { | ||
if (delimeterStack.empty()) return false; | ||
|
||
map<char, char> delimeterPairs = { | ||
{ '}', '{' }, | ||
{ ')', '(' }, | ||
{ ']', '['} | ||
}; | ||
|
||
if (delimeterPairs.find(c) == delimeterPairs.end()) return false; | ||
|
||
return delimeterStack.top() == delimeterPairs.at(c); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#pragma once | ||
|
||
#include <stack> | ||
#include <string> | ||
|
||
using namespace std; | ||
|
||
namespace Theta { | ||
class REPL { | ||
public: | ||
REPL(); | ||
~REPL(); | ||
|
||
void readInput(); | ||
|
||
#ifdef __APPLE__ | ||
static int preInputHook(const char *text, int count) { | ||
prefillIndentation(); | ||
return 0; | ||
} | ||
#else | ||
static int preInputHook() { | ||
prefillIndentation(); | ||
return 0; | ||
} | ||
#endif | ||
|
||
static void prefillIndentation(); | ||
|
||
private: | ||
static stack<char> delimeterStack; | ||
static int lineNumber; | ||
|
||
bool isMatchingDelimeter(char &c, stack<char> delimeterStack); | ||
|
||
string getPrompt(); | ||
|
||
void execute(string source); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.