dongus
This commit is contained in:
parent
75eb6fb603
commit
97bc3f447a
286
src/main.cpp
286
src/main.cpp
|
@ -20,13 +20,18 @@ enum class keywords {
|
||||||
ADD, SUBTRACT, MULTIPLY, DIVIDE,
|
ADD, SUBTRACT, MULTIPLY, DIVIDE,
|
||||||
EQUAL, INEQUAL, LESS, GREATER, EQLESS, EQGREATER,
|
EQUAL, INEQUAL, LESS, GREATER, EQLESS, EQGREATER,
|
||||||
INCREMENT, DECREMENT,
|
INCREMENT, DECREMENT,
|
||||||
PRINT, INPUT,
|
PRINT, INPUT, EXIT,
|
||||||
VALUE, SEMICOLON
|
VALUE, SEMICOLON
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Value {
|
||||||
|
valtype type;
|
||||||
|
variant<int, double, string, bool> value;
|
||||||
|
};
|
||||||
|
|
||||||
struct Token {
|
struct Token {
|
||||||
keywords keyword;
|
keywords keyword;
|
||||||
string value;
|
Value value;
|
||||||
valtype type = valtype::KEYWORD;
|
valtype type = valtype::KEYWORD;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -52,6 +57,44 @@ struct var {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool debugMode = false;
|
||||||
|
|
||||||
|
class ArgParser {
|
||||||
|
private:
|
||||||
|
vector<string> args;
|
||||||
|
public:
|
||||||
|
ArgParser(int argc, char* argv[]) {
|
||||||
|
// First, collect all arguments
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
args.push_back(argv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then process them
|
||||||
|
for (int i = 0; i < args.size(); i++) {
|
||||||
|
if (args[i] == "--debug") {
|
||||||
|
debugMode = true;
|
||||||
|
args.erase(args.begin() + i);
|
||||||
|
} else if (args[i] == "--help") {
|
||||||
|
cout << "mxlang interpreter" << endl;
|
||||||
|
cout << "Usage: mx [file]" << endl;
|
||||||
|
cout << "Options:" << endl;
|
||||||
|
cout << " --debug Enable debug mode" << endl;
|
||||||
|
cout << " --help Show this help message" << endl;
|
||||||
|
cout << "Issues? Send an email to max@maxwellj.xyz" << endl;
|
||||||
|
cout << "Report bugs at https://git.maxwellj.xyz/max/mx" << endl;
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string getArg(int index) {
|
||||||
|
if (index >= 0 && index < args.size()) {
|
||||||
|
return args[index];
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
string logList;
|
string logList;
|
||||||
|
|
||||||
class Logger {
|
class Logger {
|
||||||
|
@ -116,59 +159,70 @@ class Parser {
|
||||||
public:
|
public:
|
||||||
void parseLines(string in, Logger log) {
|
void parseLines(string in, Logger log) {
|
||||||
log.debug("Parsing lines...");
|
log.debug("Parsing lines...");
|
||||||
buffer = "";
|
buffer.clear();
|
||||||
|
termBuffer.clear();
|
||||||
|
terms.clear();
|
||||||
bool isString = false;
|
bool isString = false;
|
||||||
for (int i = 0; i < in.size(); i++) {
|
bool isEscaped = false;
|
||||||
if (in[i] == '"') {
|
|
||||||
|
for (size_t i = 0; i < in.size(); i++) {
|
||||||
|
char c = in[i];
|
||||||
|
|
||||||
|
if (isEscaped) {
|
||||||
|
buffer += c;
|
||||||
|
isEscaped = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '\\') {
|
||||||
|
isEscaped = true;
|
||||||
|
buffer += c;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '"') {
|
||||||
|
buffer += c;
|
||||||
isString = !isString;
|
isString = !isString;
|
||||||
} else if (in[i] == '\n') {
|
} else if (c == '\n' && !isString) {
|
||||||
// whole lotta nothing
|
// Skip newlines outside strings
|
||||||
} else if (in[i] == ';' && !isString) {
|
continue;
|
||||||
// Push back and clear the buffer of terms
|
} else if (c == ';' && !isString) {
|
||||||
if (!buffer.empty()) termBuffer.push_back(buffer);
|
if (!buffer.empty()) {
|
||||||
|
termBuffer.push_back(buffer);
|
||||||
|
buffer.clear();
|
||||||
|
}
|
||||||
terms.push_back(termBuffer);
|
terms.push_back(termBuffer);
|
||||||
termBuffer.clear();
|
termBuffer.clear();
|
||||||
buffer = "";
|
} else if ((c == ',' || c == '{' || c == '}' || c == '(' || c == ')') && !isString) {
|
||||||
} else if (in[i] == ',' && !isString) {
|
if (!buffer.empty()) {
|
||||||
// Push back the buffer and the ',' seperately
|
termBuffer.push_back(buffer);
|
||||||
termBuffer.push_back(buffer);
|
buffer.clear();
|
||||||
termBuffer.push_back(",");
|
}
|
||||||
buffer = "";
|
termBuffer.push_back(string(1, c));
|
||||||
} else if (in[i] == '{' && !isString) {
|
} else if (c == ' ' && !isString) {
|
||||||
// Push back the buffer and the '{' seperately
|
if (!buffer.empty()) {
|
||||||
termBuffer.push_back(buffer);
|
termBuffer.push_back(buffer);
|
||||||
termBuffer.push_back("{");
|
buffer.clear();
|
||||||
buffer = "";
|
}
|
||||||
} else if (in[i] == '}' && !isString) {
|
|
||||||
// Push back the buffer and the '}' seperately
|
|
||||||
termBuffer.push_back(buffer);
|
|
||||||
termBuffer.push_back("}");
|
|
||||||
buffer = "";
|
|
||||||
} else if (in[i] == '(' && !isString) {
|
|
||||||
// Push back the buffer and the '(' seperately
|
|
||||||
termBuffer.push_back(buffer);
|
|
||||||
termBuffer.push_back("(");
|
|
||||||
buffer = "";
|
|
||||||
} else if (in[i] == ')' && !isString) {
|
|
||||||
// Push back the buffer and the ')' seperately
|
|
||||||
termBuffer.push_back(buffer);
|
|
||||||
termBuffer.push_back(")");
|
|
||||||
buffer = "";
|
|
||||||
} else if (in[i] == ' ' && !isString) {
|
|
||||||
// Push back and reset buffer for that term
|
|
||||||
if (!buffer.empty()) termBuffer.push_back(buffer);
|
|
||||||
buffer = "";
|
|
||||||
} else {
|
} else {
|
||||||
buffer += in[i];
|
buffer += c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!buffer.empty()) termBuffer.push_back(buffer);
|
|
||||||
if (!termBuffer.empty()) terms.push_back(termBuffer);
|
// Handle any remaining buffer content
|
||||||
|
if (!buffer.empty()) {
|
||||||
|
termBuffer.push_back(buffer);
|
||||||
|
}
|
||||||
|
if (!termBuffer.empty()) {
|
||||||
|
terms.push_back(termBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug output
|
||||||
string debugString;
|
string debugString;
|
||||||
log.debug("Lines have been parsed.");
|
log.debug("Lines have been parsed.");
|
||||||
for (int i = 0; i < terms.size(); i++) {
|
for (const auto& term : terms) {
|
||||||
for (int j = 0; j < terms[i].size(); j++) {
|
for (const auto& t : term) {
|
||||||
debugString += terms[i][j] + ", ";
|
debugString += t + ", ";
|
||||||
}
|
}
|
||||||
debugString += "\n";
|
debugString += "\n";
|
||||||
}
|
}
|
||||||
|
@ -180,10 +234,12 @@ class Parser {
|
||||||
for (int j = 0; j < terms[i].size(); j++) {
|
for (int j = 0; j < terms[i].size(); j++) {
|
||||||
Token token;
|
Token token;
|
||||||
string ct = terms[i][j];
|
string ct = terms[i][j];
|
||||||
if (ct == " ") {}
|
if (ct == " ") continue;
|
||||||
// Oh boy here we go
|
// Oh boy here we go
|
||||||
else if (ct == "fun") token.keyword = keywords::FUN;
|
else if (ct == "fun") token.keyword = keywords::FUN;
|
||||||
|
else if (ct == "print") token.keyword = keywords::PRINT;
|
||||||
else if (ct == "return") token.keyword = keywords::RETURN;
|
else if (ct == "return") token.keyword = keywords::RETURN;
|
||||||
|
else if (ct == "exit") token.keyword = keywords::EXIT;
|
||||||
else if (ct == "int") token.keyword = keywords::INT;
|
else if (ct == "int") token.keyword = keywords::INT;
|
||||||
else if (ct == "{") token.keyword = keywords::OBRAC;
|
else if (ct == "{") token.keyword = keywords::OBRAC;
|
||||||
else if (ct == "}") token.keyword = keywords::CBRAC;
|
else if (ct == "}") token.keyword = keywords::CBRAC;
|
||||||
|
@ -195,10 +251,33 @@ class Parser {
|
||||||
else if (ct == "/") token.keyword = keywords::DIVIDE;
|
else if (ct == "/") token.keyword = keywords::DIVIDE;
|
||||||
else {
|
else {
|
||||||
token.keyword = keywords::VALUE;
|
token.keyword = keywords::VALUE;
|
||||||
token.value = ct;
|
// Convert the value based on its type
|
||||||
if (canInt(ct)) token.type = valtype::INT;
|
if (canDec(ct)) {
|
||||||
if (canDec(ct)) token.type = valtype::DEC;
|
token.type = valtype::DEC;
|
||||||
if (ct == "true" || ct == "false") token.type = valtype::BOOL;
|
token.value.type = valtype::DEC;
|
||||||
|
token.value.value = stod(ct);
|
||||||
|
}
|
||||||
|
else if (canInt(ct)) {
|
||||||
|
token.type = valtype::INT;
|
||||||
|
token.value.type = valtype::INT;
|
||||||
|
token.value.value = stoi(ct);
|
||||||
|
}
|
||||||
|
else if (ct == "true" || ct == "false") {
|
||||||
|
token.type = valtype::BOOL;
|
||||||
|
token.value.type = valtype::BOOL;
|
||||||
|
token.value.value = (ct == "true");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Handle strings - remove quotes if present
|
||||||
|
token.type = valtype::STR;
|
||||||
|
token.value.type = valtype::STR;
|
||||||
|
if (ct.size() >= 2 && ct.front() == '"' && ct.back() == '"') {
|
||||||
|
// Remove the quotes
|
||||||
|
token.value.value = ct.substr(1, ct.size() - 2);
|
||||||
|
} else {
|
||||||
|
token.value.value = ct;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tokens.push_back(token);
|
tokens.push_back(token);
|
||||||
}
|
}
|
||||||
|
@ -206,7 +285,7 @@ class Parser {
|
||||||
semi.keyword = keywords::SEMICOLON;
|
semi.keyword = keywords::SEMICOLON;
|
||||||
tokens.push_back(semi);
|
tokens.push_back(semi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vector<Token> getTokens() {
|
vector<Token> getTokens() {
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
@ -216,56 +295,113 @@ class Interpreter {
|
||||||
private:
|
private:
|
||||||
vector<Token> tokens;
|
vector<Token> tokens;
|
||||||
Logger log;
|
Logger log;
|
||||||
int tokenIndex = 0;
|
int tokenIndex = -1;
|
||||||
optional<Token> consume() {
|
optional<Token> consume() {
|
||||||
tokenIndex++;
|
tokenIndex++;
|
||||||
if (tokens.size() > tokenIndex) return tokens.at(tokenIndex);
|
if (tokenIndex < tokens.size()) return tokens[tokenIndex];
|
||||||
else return {};
|
return {};
|
||||||
}
|
}
|
||||||
optional<Token> peek(int index = 1) {
|
optional<Token> peek(int offset = 1) {
|
||||||
if (tokens.size() > tokenIndex + index) return tokens.at(tokenIndex + index);
|
int index = tokenIndex + offset;
|
||||||
else return {};
|
if (index >= 0 && index < tokens.size()) return tokens[index];
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void interpret(vector<Token> tokenList) {
|
void convertToTokens(vector<Token> tokenList) {
|
||||||
tokens = tokenList;
|
tokens = tokenList;
|
||||||
log.debug("Alright we got " + to_string(tokens.size()) + " tokens");
|
log.debug("Alright we got " + to_string(tokens.size()) + " tokens");
|
||||||
while (tokenIndex < tokens.size()) {
|
|
||||||
if (peek().has_value()) Token currentToken = consume().value();
|
|
||||||
else break;
|
|
||||||
int peekAhead = 1;
|
|
||||||
vector<Token> currentInstruction;
|
|
||||||
if (peek(peekAhead).has_value()) while (peek(peekAhead).value().keyword != keywords::SEMICOLON) {
|
|
||||||
if (peek(peekAhead).has_value()) {
|
|
||||||
currentInstruction.push_back(peek(peekAhead).value());
|
|
||||||
peekAhead ++;
|
|
||||||
} else break;
|
|
||||||
}
|
|
||||||
consume();
|
|
||||||
|
|
||||||
|
while (tokenIndex < static_cast<int>(tokens.size() - 1)) {
|
||||||
|
auto currentToken = consume();
|
||||||
|
if (!currentToken) break;
|
||||||
|
|
||||||
|
vector<Token> currentInstruction;
|
||||||
|
currentInstruction.push_back(currentToken.value());
|
||||||
|
|
||||||
|
// Collect tokens until semicolon
|
||||||
|
while (auto nextToken = peek(1)) {
|
||||||
|
if (nextToken->keyword == keywords::SEMICOLON) {
|
||||||
|
consume(); // consume the semicolon
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
consume(); // consume the peeked token
|
||||||
|
currentInstruction.push_back(nextToken.value());
|
||||||
|
}
|
||||||
|
// Execute the instruction
|
||||||
|
executeCode(currentInstruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void executeCode(vector<Token> tokens) {
|
||||||
|
for (int i = 0; i < tokens.size(); i++) {
|
||||||
|
if (tokens[i].keyword == keywords::PRINT) {
|
||||||
|
i++;
|
||||||
|
if (tokens.size() <= i) break;
|
||||||
|
Token nextToken = tokens[i];
|
||||||
|
// Handle different value types
|
||||||
|
if (nextToken.keyword == keywords::VALUE) {
|
||||||
|
switch (nextToken.type) {
|
||||||
|
case valtype::INT:
|
||||||
|
cout << get<int>(nextToken.value.value) << endl;
|
||||||
|
break;
|
||||||
|
case valtype::DEC:
|
||||||
|
cout << get<double>(nextToken.value.value) << endl;
|
||||||
|
break;
|
||||||
|
case valtype::STR:
|
||||||
|
cout << get<string>(nextToken.value.value) << endl;
|
||||||
|
break;
|
||||||
|
case valtype::BOOL:
|
||||||
|
cout << (get<bool>(nextToken.value.value) ? "true" : "false") << endl;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log.error("Unsupported value type");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.error("Expected a value after print statement");
|
||||||
|
}
|
||||||
|
} else if (tokens[i].keyword == keywords::EXIT) {
|
||||||
|
i++;
|
||||||
|
if (tokens.size() <= i) break;
|
||||||
|
Token nextToken = tokens[i];
|
||||||
|
if (nextToken.keyword == keywords::VALUE) {
|
||||||
|
switch (nextToken.type) {
|
||||||
|
case valtype::INT:
|
||||||
|
exit(get<int>(nextToken.value.value));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log.error("Unsupported value type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
// Initialise the logger and parser
|
// Parse and act upon command line arguments
|
||||||
|
ArgParser args(argc, argv);
|
||||||
|
// Initialise the logger
|
||||||
Logger log;
|
Logger log;
|
||||||
log.toggleDebugPrint();
|
if (debugMode) log.toggleDebugPrint();
|
||||||
log.debug("Logger initialised!");
|
log.debug("Logger initialised!");
|
||||||
|
// Initialise the parser
|
||||||
Parser parser;
|
Parser parser;
|
||||||
// Exit if file doesn't exist
|
// Exit if file doesn't exist
|
||||||
if (argc < 2) log.fatalError("Please provide a file", 1);
|
if (argc < 2) log.fatalError("Please provide a file", 1);
|
||||||
// Read the file
|
// Read the file
|
||||||
ifstream inputFile(argv[1]);
|
ifstream inputFile(args.getArg(1));
|
||||||
string input;
|
string input;
|
||||||
string file;
|
string file;
|
||||||
while(getline(inputFile, input)) {
|
while(getline(inputFile, input)) {
|
||||||
file += input;
|
file += input;
|
||||||
}
|
}
|
||||||
|
inputFile.close();
|
||||||
|
// Parse the file
|
||||||
parser.parseLines(file, log);
|
parser.parseLines(file, log);
|
||||||
parser.processLines();
|
parser.processLines();
|
||||||
|
// Initialise the interpreter
|
||||||
Interpreter interpreter;
|
Interpreter interpreter;
|
||||||
interpreter.interpret(parser.getTokens());
|
// Convert to tokens and run the code
|
||||||
|
interpreter.convertToTokens(parser.getTokens());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user