dongus
This commit is contained in:
parent
75eb6fb603
commit
97bc3f447a
282
src/main.cpp
282
src/main.cpp
|
@ -20,13 +20,18 @@ enum class keywords {
|
|||
ADD, SUBTRACT, MULTIPLY, DIVIDE,
|
||||
EQUAL, INEQUAL, LESS, GREATER, EQLESS, EQGREATER,
|
||||
INCREMENT, DECREMENT,
|
||||
PRINT, INPUT,
|
||||
PRINT, INPUT, EXIT,
|
||||
VALUE, SEMICOLON
|
||||
};
|
||||
|
||||
struct Value {
|
||||
valtype type;
|
||||
variant<int, double, string, bool> value;
|
||||
};
|
||||
|
||||
struct Token {
|
||||
keywords keyword;
|
||||
string value;
|
||||
Value value;
|
||||
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;
|
||||
|
||||
class Logger {
|
||||
|
@ -116,59 +159,70 @@ class Parser {
|
|||
public:
|
||||
void parseLines(string in, Logger log) {
|
||||
log.debug("Parsing lines...");
|
||||
buffer = "";
|
||||
buffer.clear();
|
||||
termBuffer.clear();
|
||||
terms.clear();
|
||||
bool isString = false;
|
||||
for (int i = 0; i < in.size(); i++) {
|
||||
if (in[i] == '"') {
|
||||
bool isEscaped = false;
|
||||
|
||||
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;
|
||||
} else if (in[i] == '\n') {
|
||||
// whole lotta nothing
|
||||
} else if (in[i] == ';' && !isString) {
|
||||
// Push back and clear the buffer of terms
|
||||
if (!buffer.empty()) termBuffer.push_back(buffer);
|
||||
} else if (c == '\n' && !isString) {
|
||||
// Skip newlines outside strings
|
||||
continue;
|
||||
} else if (c == ';' && !isString) {
|
||||
if (!buffer.empty()) {
|
||||
termBuffer.push_back(buffer);
|
||||
buffer.clear();
|
||||
}
|
||||
terms.push_back(termBuffer);
|
||||
termBuffer.clear();
|
||||
buffer = "";
|
||||
} else if (in[i] == ',' && !isString) {
|
||||
// Push back the buffer and the ',' seperately
|
||||
} else if ((c == ',' || c == '{' || c == '}' || c == '(' || c == ')') && !isString) {
|
||||
if (!buffer.empty()) {
|
||||
termBuffer.push_back(buffer);
|
||||
termBuffer.push_back(",");
|
||||
buffer = "";
|
||||
} else if (in[i] == '{' && !isString) {
|
||||
// Push back the buffer and the '{' seperately
|
||||
buffer.clear();
|
||||
}
|
||||
termBuffer.push_back(string(1, c));
|
||||
} else if (c == ' ' && !isString) {
|
||||
if (!buffer.empty()) {
|
||||
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 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 = "";
|
||||
buffer.clear();
|
||||
}
|
||||
} 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;
|
||||
log.debug("Lines have been parsed.");
|
||||
for (int i = 0; i < terms.size(); i++) {
|
||||
for (int j = 0; j < terms[i].size(); j++) {
|
||||
debugString += terms[i][j] + ", ";
|
||||
for (const auto& term : terms) {
|
||||
for (const auto& t : term) {
|
||||
debugString += t + ", ";
|
||||
}
|
||||
debugString += "\n";
|
||||
}
|
||||
|
@ -180,10 +234,12 @@ class Parser {
|
|||
for (int j = 0; j < terms[i].size(); j++) {
|
||||
Token token;
|
||||
string ct = terms[i][j];
|
||||
if (ct == " ") {}
|
||||
if (ct == " ") continue;
|
||||
// Oh boy here we go
|
||||
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 == "exit") token.keyword = keywords::EXIT;
|
||||
else if (ct == "int") token.keyword = keywords::INT;
|
||||
else if (ct == "{") token.keyword = keywords::OBRAC;
|
||||
else if (ct == "}") token.keyword = keywords::CBRAC;
|
||||
|
@ -195,10 +251,33 @@ class Parser {
|
|||
else if (ct == "/") token.keyword = keywords::DIVIDE;
|
||||
else {
|
||||
token.keyword = keywords::VALUE;
|
||||
token.value = ct;
|
||||
if (canInt(ct)) token.type = valtype::INT;
|
||||
if (canDec(ct)) token.type = valtype::DEC;
|
||||
if (ct == "true" || ct == "false") token.type = valtype::BOOL;
|
||||
// Convert the value based on its type
|
||||
if (canDec(ct)) {
|
||||
token.type = valtype::DEC;
|
||||
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);
|
||||
}
|
||||
|
@ -206,7 +285,7 @@ class Parser {
|
|||
semi.keyword = keywords::SEMICOLON;
|
||||
tokens.push_back(semi);
|
||||
}
|
||||
}
|
||||
}
|
||||
vector<Token> getTokens() {
|
||||
return tokens;
|
||||
}
|
||||
|
@ -216,56 +295,113 @@ class Interpreter {
|
|||
private:
|
||||
vector<Token> tokens;
|
||||
Logger log;
|
||||
int tokenIndex = 0;
|
||||
int tokenIndex = -1;
|
||||
optional<Token> consume() {
|
||||
tokenIndex++;
|
||||
if (tokens.size() > tokenIndex) return tokens.at(tokenIndex);
|
||||
else return {};
|
||||
if (tokenIndex < tokens.size()) return tokens[tokenIndex];
|
||||
return {};
|
||||
}
|
||||
optional<Token> peek(int index = 1) {
|
||||
if (tokens.size() > tokenIndex + index) return tokens.at(tokenIndex + index);
|
||||
else return {};
|
||||
optional<Token> peek(int offset = 1) {
|
||||
int index = tokenIndex + offset;
|
||||
if (index >= 0 && index < tokens.size()) return tokens[index];
|
||||
return {};
|
||||
}
|
||||
|
||||
public:
|
||||
void interpret(vector<Token> tokenList) {
|
||||
void convertToTokens(vector<Token> tokenList) {
|
||||
tokens = tokenList;
|
||||
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[]) {
|
||||
// Initialise the logger and parser
|
||||
// Parse and act upon command line arguments
|
||||
ArgParser args(argc, argv);
|
||||
// Initialise the logger
|
||||
Logger log;
|
||||
log.toggleDebugPrint();
|
||||
if (debugMode) log.toggleDebugPrint();
|
||||
log.debug("Logger initialised!");
|
||||
// Initialise the parser
|
||||
Parser parser;
|
||||
// Exit if file doesn't exist
|
||||
if (argc < 2) log.fatalError("Please provide a file", 1);
|
||||
// Read the file
|
||||
ifstream inputFile(argv[1]);
|
||||
ifstream inputFile(args.getArg(1));
|
||||
string input;
|
||||
string file;
|
||||
while(getline(inputFile, input)) {
|
||||
file += input;
|
||||
}
|
||||
inputFile.close();
|
||||
// Parse the file
|
||||
parser.parseLines(file, log);
|
||||
parser.processLines();
|
||||
// Initialise the interpreter
|
||||
Interpreter interpreter;
|
||||
interpreter.interpret(parser.getTokens());
|
||||
// Convert to tokens and run the code
|
||||
interpreter.convertToTokens(parser.getTokens());
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user