Skip to content

Commit

Permalink
Merge pull request #44 from alexdovzhanyn/repl-code-execution
Browse files Browse the repository at this point in the history
REPL code execution
  • Loading branch information
alexdovzhanyn authored Oct 4, 2024
2 parents a18add6 + bbf002b commit dc66e7c
Show file tree
Hide file tree
Showing 9 changed files with 271 additions and 122 deletions.
193 changes: 84 additions & 109 deletions src/cli/CLI.cpp
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;
}
21 changes: 21 additions & 0 deletions src/cli/CLI.hpp
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);
};
}
110 changes: 110 additions & 0 deletions src/cli/REPL.cpp
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);
}
40 changes: 40 additions & 0 deletions src/cli/REPL.hpp
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);
};
}
2 changes: 1 addition & 1 deletion src/compiler/CodeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#include "parser/ast/FunctionDeclarationNode.hpp"
#include "parser/ast/IdentifierNode.hpp"
#include "parser/ast/TypeDeclarationNode.hpp"
#include "cli/CLI.cpp"
#include "cli/CLI.hpp"

#ifdef __APPLE__
#include <mach-o/dyld.h>
Expand Down
Loading

0 comments on commit dc66e7c

Please sign in to comment.