diff --git a/main b/main index 4dbfaee..adccdbc 100755 Binary files a/main and b/main differ diff --git a/mx b/mx index f98e188..a8dd211 100755 Binary files a/mx and b/mx differ diff --git a/src/main.cpp b/src/main.cpp index e768a83..2dc7530 100644 --- a/src/main.cpp +++ b/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 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 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 - 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 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 if ((c == ',' || c == '{' || c == '}' || c == '(' || c == ')') && !isString) { + if (!buffer.empty()) { + termBuffer.push_back(buffer); + buffer.clear(); + } + termBuffer.push_back(string(1, c)); + } else if (c == ' ' && !isString) { + if (!buffer.empty()) { + termBuffer.push_back(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 getTokens() { return tokens; } @@ -216,56 +295,113 @@ class Interpreter { private: vector tokens; Logger log; - int tokenIndex = 0; + int tokenIndex = -1; optional consume() { tokenIndex++; - if (tokens.size() > tokenIndex) return tokens.at(tokenIndex); - else return {}; + if (tokenIndex < tokens.size()) return tokens[tokenIndex]; + return {}; } - optional peek(int index = 1) { - if (tokens.size() > tokenIndex + index) return tokens.at(tokenIndex + index); - else return {}; + optional peek(int offset = 1) { + int index = tokenIndex + offset; + if (index >= 0 && index < tokens.size()) return tokens[index]; + return {}; } public: - void interpret(vector tokenList) { + void convertToTokens(vector 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; + + while (tokenIndex < static_cast(tokens.size() - 1)) { + auto currentToken = consume(); + if (!currentToken) break; + vector 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; + 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 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(nextToken.value.value) << endl; + break; + case valtype::DEC: + cout << get(nextToken.value.value) << endl; + break; + case valtype::STR: + cout << get(nextToken.value.value) << endl; + break; + case valtype::BOOL: + cout << (get(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(nextToken.value.value)); + break; + default: + log.error("Unsupported value type"); + } + } } - consume(); - } } }; 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; -} +} \ No newline at end of file diff --git a/test.mx b/test.mx index 70b2168..bd922a6 100644 --- a/test.mx +++ b/test.mx @@ -1,3 +1,2 @@ -fun int main() { - return 1; -} +print "Hello world!"; +exit 1; \ No newline at end of file