This commit is contained in:
Maxwell Jeffress 2025-04-22 21:09:55 +10:00
parent 75eb6fb603
commit 97bc3f447a
4 changed files with 213 additions and 78 deletions

BIN
main

Binary file not shown.

BIN
mx

Binary file not shown.

View File

@ -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;
} }

View File

@ -1,3 +1,2 @@
fun int main() { print "Hello world!";
return 1; exit 1;
}