This repository has been archived on 2025-09-13. You can view files and clone it, but cannot push or open issues or pull requests.
Files
iodine/src/Interpreter.cpp

681 lines
35 KiB
C++
Raw Normal View History

2025-05-10 21:28:56 +10:00
/*
Iodine
Copyright (C) 2025 Maxwell Jeffress
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "Interpreter.h"
#include "common.h"
optional<Token> Interpreter::consume() {
tokenIndex++;
if (tokenIndex < tokens.size()) return tokens[tokenIndex];
return {};
}
optional<Token> Interpreter::peek(int offset) {
int index = tokenIndex + offset;
if (index >= 0 && index < tokens.size()) return tokens[index];
return {};
}
void Interpreter::initInterpreter(vector<string> args) {
List arguments;
2025-05-13 11:06:40 +10:00
arguments.type = valtype::STR;
for (int i = 0; i < args.size(); i++) {
Value buf;
buf.type = valtype::STR;
buf.value = args[i];
arguments.value.push_back(buf);
}
Value buf;
buf.type = valtype::LIST;
buf.value = arguments;
variables["args"] = buf;
}
2025-05-16 06:17:57 +10:00
valtype Interpreter::keywordToValtype(keywords in) {
switch (in) {
case keywords::INT: return valtype::INT;
case keywords::DEC: return valtype::DEC;
case keywords::BOOL: return valtype::BOOL;
case keywords::STR: return valtype::STR;
default: return valtype::KEYWORD;
}
return valtype::KEYWORD;
}
2025-05-20 15:03:08 +10:00
optional<Token> Interpreter::interpret(vector<Token> tokenList, bool isFunction) {
2025-05-10 21:28:56 +10:00
if (debugMode) log.toggleDebugPrint();
2025-05-20 15:03:08 +10:00
if (isFunction) log.debug("Currently executing code for a function");
2025-05-10 21:28:56 +10:00
tokens = tokenList;
log.debug("Alright we got " + to_string(tokens.size()) + " tokens");
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 || nextToken->keyword == keywords::OBRAC || nextToken->keyword == keywords::CBRAC) {
consume(); // consume the semicolon
break;
} else if (nextToken->keyword == keywords::COMMENT) {
break;
}
consume(); // consume the peeked token
currentInstruction.push_back(nextToken.value());
lengthOfLine ++;
}
// Apply variables to tokens, as well as allow users to input strings
// We start at 1 so we can reassign variables in the execution of code
for (int i = 1; i < currentInstruction.size(); i++) {
if (currentInstruction[i].type == valtype::STR) {
string potentialVarName = get<string>(currentInstruction[i].value.value);
auto varIt = variables.find(potentialVarName);
if (varIt != variables.end()) {
2025-05-13 11:06:40 +10:00
Token newToken;
newToken.keyword = keywords::VALUE;
if (varIt->second.type == valtype::LIST) newToken.keyword = keywords::LISTOBJ;
newToken.type = varIt->second.type;
newToken.value = varIt->second;
currentInstruction[i] = newToken;
2025-05-10 21:28:56 +10:00
}
} else if (currentInstruction[i].keyword == keywords::INPUT) {
Token newToken;
string buffer;
getline(cin, buffer);
newToken.value.value = buffer;
newToken.keyword = keywords::VALUE;
newToken.type = valtype::STR;
newToken.value.type = valtype::STR;
currentInstruction[i] = newToken;
}
}
2025-05-13 11:06:40 +10:00
// Allow users to get elements from list objects
for (int i = 0; i < currentInstruction.size(); i++) {
if (currentInstruction[i].keyword == keywords::LISTOBJ || currentInstruction[i].value.type == valtype::LIST) {
log.debug("Found a list object");
if (i + 3 < currentInstruction.size()) {
log.debug("Instruction is large enough to have a list index");
if (currentInstruction[i + 1].keyword == keywords::OSQUA && currentInstruction[i + 3].keyword == keywords::CSQUA && currentInstruction[i + 2].value.type == valtype::INT) {
log.debug("Finding list object item");
if (currentInstruction[i + 2].value.type != valtype::INT) syntaxError.generalError("List index requires an int");
if (get<List>(currentInstruction[i].value.value).value.size() < get<int>(currentInstruction[i + 2].value.value)) syntaxError.listOutOfBounds();
Token newToken;
newToken.keyword = keywords::VALUE;
newToken.type = get<List>(currentInstruction[i].value.value).type;
newToken.value.type = get<List>(currentInstruction[i].value.value).type;
log.debug("Writing type " + log.getTypeString(newToken.type));
newToken.value = get<List>(currentInstruction[i].value.value).value[get<int>(currentInstruction[i + 2].value.value)];
currentInstruction[i] = newToken;
currentInstruction.erase(currentInstruction.begin() + i + 1);
currentInstruction.erase(currentInstruction.begin() + i + 1);
currentInstruction.erase(currentInstruction.begin() + i + 1);
}
}
}
}
2025-05-10 21:28:56 +10:00
// Do math
for (int i = 0; i < currentInstruction.size(); i++) {
if (currentInstruction[i].keyword == keywords::ADD || currentInstruction[i].keyword == keywords::SUBTRACT || currentInstruction[i].keyword == keywords::MULTIPLY || currentInstruction[i].keyword == keywords::DIVIDE) {
Token newToken;
newToken.keyword = keywords::VALUE;
if (currentInstruction.size() < i + 1) syntaxError.mathTooFewArgs();
Token before = currentInstruction[i - 1];
Token after = currentInstruction[i + 1];
if (before.type != after.type) syntaxError.mathTypeMismatch();
newToken.type = before.type;
if (currentInstruction[i].keyword == keywords::ADD) {
if (newToken.type == valtype::INT) {
newToken.value.value = get<int>(before.value.value) + get<int>(after.value.value);
}
else if (newToken.type == valtype::DEC) {
newToken.value.value = get<double>(before.value.value) + get<double>(after.value.value);
}
else if (newToken.type == valtype::STR) {
newToken.value.value = get<string>(before.value.value) + get<string>(after.value.value);
} else {
syntaxError.mathCannotDoOperationOnType("+", "bool");
}
}
else if (currentInstruction[i].keyword == keywords::SUBTRACT) {
if (newToken.type == valtype::INT) {
newToken.value.value = get<int>(before.value.value) - get<int>(after.value.value);
}
else if (newToken.type == valtype::DEC) {
newToken.value.value = get<double>(before.value.value) - get<double>(after.value.value);
} else {
syntaxError.mathCannotDoOperationOnType("-", "bool or string");
}
}
else if (currentInstruction[i].keyword == keywords::MULTIPLY) {
if (newToken.type == valtype::INT) {
newToken.value.value = get<int>(before.value.value) * get<int>(after.value.value);
}
else if (newToken.type == valtype::DEC) {
newToken.value.value = get<double>(before.value.value) * get<double>(after.value.value);
} else {
syntaxError.mathCannotDoOperationOnType("*", "bool or string");
}
}
else if (currentInstruction[i].keyword == keywords::DIVIDE) {
if (newToken.type == valtype::INT) {
newToken.value.value = get<int>(before.value.value) / get<int>(after.value.value);
}
else if (newToken.type == valtype::DEC) {
newToken.value.value = get<double>(before.value.value) / get<double>(after.value.value);
} else {
syntaxError.mathCannotDoOperationOnType("/", "bool or string");
}
} else {
// Something has gone terribly wrong
// We should never reach this point in the code
syntaxError.generalError("The math aint mathing");
}
// Insert our cool new token and get rid of the boring old stuff
currentInstruction[i - 1] = newToken;
currentInstruction.erase(currentInstruction.begin() + i);
currentInstruction.erase(currentInstruction.begin() + i);
i -= 1;
}
}
2025-05-20 15:03:08 +10:00
2025-05-10 21:28:56 +10:00
// Detect any comparisons and convert
// Start at 1 to avoid having to do more crappy memory safety stuff
for (int i = 1; i < currentInstruction.size(); i++) {
if (currentInstruction[i].keyword == keywords::EQUAL) {
Token newToken;
newToken.keyword = keywords::VALUE;
newToken.type = valtype::BOOL;
newToken.value.type = valtype::BOOL;
if (currentInstruction.size() < i + 1) syntaxError.mathTooFewArgs();
if (currentInstruction[i - 1].type != currentInstruction[i + 1].type) syntaxError.mathTypeMismatch();
log.debug("Comparing values of type: " + to_string(static_cast<int>(currentInstruction[i - 1].type)));
// Make sure both operands are values
if (currentInstruction[i - 1].keyword != keywords::VALUE ||
currentInstruction[i + 1].keyword != keywords::VALUE) {
syntaxError.generalError("Can only compare values");
2025-05-20 15:03:08 +10:00
return {};
2025-05-10 21:28:56 +10:00
}
// Ensure value types are set correctly
if (currentInstruction[i - 1].value.type == valtype::INT) {
newToken.value.value = (get<int>(currentInstruction[i - 1].value.value) ==
get<int>(currentInstruction[i + 1].value.value));
} else if (currentInstruction[i - 1].value.type == valtype::DEC) {
newToken.value.value = (get<double>(currentInstruction[i - 1].value.value) ==
get<double>(currentInstruction[i + 1].value.value));
} else if (currentInstruction[i - 1].value.type == valtype::STR) {
newToken.value.value = (get<string>(currentInstruction[i - 1].value.value) ==
get<string>(currentInstruction[i + 1].value.value));
} else if (currentInstruction[i - 1].value.type == valtype::BOOL) {
newToken.value.value = (get<bool>(currentInstruction[i - 1].value.value) ==
get<bool>(currentInstruction[i + 1].value.value));
}
currentInstruction[i - 1] = newToken;
currentInstruction.erase(currentInstruction.begin() + i);
currentInstruction.erase(currentInstruction.begin() + i);
i -= 1;
}
if (currentInstruction[i].keyword == keywords::INEQUAL) {
Token newToken;
newToken.keyword = keywords::VALUE;
newToken.type = valtype::BOOL;
newToken.value.type = valtype::BOOL;
if (currentInstruction.size() < i + 1) syntaxError.mathTooFewArgs();
if (currentInstruction[i - 1].type != currentInstruction[i + 1].type) syntaxError.mathTypeMismatch();
log.debug("Comparing values of type: " + to_string(static_cast<int>(currentInstruction[i - 1].type)));
// Make sure both operands are values
if (currentInstruction[i - 1].keyword != keywords::VALUE ||
currentInstruction[i + 1].keyword != keywords::VALUE) {
syntaxError.generalError("Can only compare values of");
2025-05-20 15:03:08 +10:00
return {};
2025-05-10 21:28:56 +10:00
}
// Ensure value types are set correctly
if (currentInstruction[i - 1].value.type == valtype::INT) {
newToken.value.value = (get<int>(currentInstruction[i - 1].value.value) !=
get<int>(currentInstruction[i + 1].value.value));
} else if (currentInstruction[i - 1].value.type == valtype::DEC) {
newToken.value.value = (get<double>(currentInstruction[i - 1].value.value) !=
get<double>(currentInstruction[i + 1].value.value));
} else if (currentInstruction[i - 1].value.type == valtype::STR) {
newToken.value.value = (get<string>(currentInstruction[i - 1].value.value) !=
get<string>(currentInstruction[i + 1].value.value));
} else if (currentInstruction[i - 1].value.type == valtype::BOOL) {
newToken.value.value = (get<bool>(currentInstruction[i - 1].value.value) !=
get<bool>(currentInstruction[i + 1].value.value));
}
currentInstruction[i - 1] = newToken;
currentInstruction.erase(currentInstruction.begin() + i);
currentInstruction.erase(currentInstruction.begin() + i);
i -= 1;
}
}
2025-05-12 20:04:38 +10:00
// Find lists and create list objects
for (int i = 0; i < currentInstruction.size(); i++) {
2025-05-13 14:05:29 +10:00
if (currentInstruction[i].keyword == keywords::OSQUA && currentInstruction[i + 1].keyword != keywords::CSQUA) {
2025-05-12 20:04:38 +10:00
log.debug("Making a list");
int startIndex = i;
currentInstruction.erase(currentInstruction.begin() + i);
List buf;
buf.type = currentInstruction[i].value.type;
while (currentInstruction[i].keyword != keywords::CSQUA) {
if (buf.type == currentInstruction[i].value.type) {
buf.value.push_back(currentInstruction[i].value);
currentInstruction.erase(currentInstruction.begin() + i);
} else {
syntaxError.listTypeMismatch();
}
}
currentInstruction.erase(currentInstruction.begin() + i);
Value newValue;
newValue.type = valtype::LIST;
newValue.value = buf;
Token newToken;
newToken.type = valtype::LIST;
newToken.value.type = valtype::LIST;
newToken.value.value = buf;
newToken.keyword = keywords::LISTOBJ;
2025-05-12 20:04:38 +10:00
currentInstruction.insert(currentInstruction.begin() + startIndex, newToken);
}
}
2025-05-20 15:03:08 +10:00
// Call functions and assign values
for (int i = 0; i < currentInstruction.size(); i++) {
log.debug("Looking for a function");
Function empty;
for (int j = 0; j < functionnames.size(); j++) {
log.debug("There is a function called " + functionnames[j]);
//if (tokens[i].type == valtype::STR && functionnames[j] == get<string>(tokens[i].value.value)) log.debug("in theory we should be fine?");
}
if (currentInstruction[i].type == valtype::STR && functions.find(get<string>(currentInstruction[i].value.value)) != functions.end()) {
log.debug("Found a function");
Function fnbuf = functions[get<string>(currentInstruction[i].value.value)];
i++;
if (currentInstruction[i].keyword != keywords::OPARE) syntaxError.generalError("Expecting a '(' when calling a function");
i++;
vector<Token> argsForFn;
int argCount = 0;
if (currentInstruction[i].keyword != keywords::CPARE) while (currentInstruction[i].keyword != keywords::CPARE) {
if (i >= currentInstruction.size()) syntaxError.generalError("Expecting a ')' after function arguments");
if (currentInstruction[i].value.type != fnbuf.arguments[fnbuf.argNames[argCount]]) syntaxError.fnTypeMismatch(get<string>(currentInstruction[i].value.value), {log.getTypeString(fnbuf.arguments[fnbuf.argNames[argCount]])}, currentInstruction[i].value.type);
variables[fnbuf.argNames[argCount]] = tokens[i].value;
i++;
}
resumePoint.push(tokenIndex);
vector<Token> fnTokens;
int l = 1;
int m = 0;
while (l > 0) {
if (tokens[m].keyword == keywords::OBRAC) l++;
if (tokens[m].keyword == keywords::CBRAC) l--;
if (l < 1) break;
fnTokens.push_back(tokens[m]);
m++;
}
interpret(fnTokens, true);
log.debug("This is a test call for a function. Function won't actually be run yet. If you see this everything is fine");
//tokenIndex = functions[get<string>(currentInstruction[i].value.value)].startToken;
}
}
2025-05-10 21:28:56 +10:00
// Execute the instruction
log.debug("Length of line is " + to_string(lengthOfLine));
2025-05-20 15:03:08 +10:00
executeCode(currentInstruction, isFunction);
2025-05-10 21:28:56 +10:00
lengthOfLine = 0;
}
}
2025-05-20 15:03:08 +10:00
optional<Token> Interpreter::executeCode(vector<Token> tokens, bool isFunction) {
2025-05-10 21:28:56 +10:00
SyntaxError syntaxError;
log.debug("Token length for this expression is " + to_string(tokens.size()));
for (int i = 0; i < tokens.size(); i++) {
if (skipScope > 0) {
int braceCount = 0;
while (i < tokens.size()) {
2025-05-11 13:55:52 +10:00
if (tokens[i].keyword == keywords::OBRAC) {
braceCount ++;
} else if (tokens[i].keyword == keywords::CBRAC) {
if (braceCount == 0) {
skipScope --;
break;
} else {
braceCount --;
2025-05-10 21:28:56 +10:00
}
}
2025-05-11 13:55:52 +10:00
i++;
2025-05-10 21:28:56 +10:00
}
}
if (repeatScope > 0) {
if (tokens[i].keyword == keywords::CBRAC) {
tokenIndex = loop;
repeatScope --;
2025-05-20 15:03:08 +10:00
return {};
2025-05-10 21:28:56 +10:00
}
}
if (tokens[i].keyword == keywords::PRINTLN) {
i++;
2025-05-12 20:04:38 +10:00
if (tokens.size() <= i) syntaxError.fnNotSufficientArgs("println", 1, 1, 0);
2025-05-10 21:28:56 +10:00
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:
vector<string> validTypes = {"int", "dec", "str", "bool"};
syntaxError.fnTypeMismatch("println", validTypes, nextToken.type);
}
} else {
2025-05-12 20:04:38 +10:00
syntaxError.fnTypeMismatch("println", {}, nextToken.type);
2025-05-10 21:28:56 +10:00
}
} else 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);
break;
case valtype::DEC:
cout << get<double>(nextToken.value.value);
break;
case valtype::STR:
cout << get<string>(nextToken.value.value);
break;
case valtype::BOOL:
cout << (get<bool>(nextToken.value.value) ? "true" : "false");
break;
default:
vector<string> validTypes = {"int", "dec", "str", "bool"};
syntaxError.fnTypeMismatch("print", validTypes, nextToken.type);
}
} else {
syntaxError.fnNotSufficientArgs("print", 1, 1, 0);
}
2025-05-12 20:04:38 +10:00
} else if (tokens[i].keyword == keywords::PRINTLIST) {
i++;
if (tokens.size() <= i) syntaxError.fnNotSufficientArgs("printlist", 1, 1, 0);
Token nextToken = tokens[i];
log.debug("Printing a list. Type of next token is " + log.getTypeString(nextToken.value.type));
if (nextToken.keyword == keywords::LISTOBJ && nextToken.type == valtype::LIST) {
2025-05-12 20:04:38 +10:00
List list = get<List>(nextToken.value.value);
log.debug("List obtained");
if (list.value[0].type == valtype::INT) {
for (int j = 0; j < list.value.size(); j++) {
cout << get<int>(list.value[j].value) << ", ";
}
}
else if (list.value[0].type == valtype::DEC) {
for (int j = 0; j < list.value.size(); j++) {
cout << get<double>(list.value[j].value) << ", ";
}
}
else if (list.value[0].type == valtype::STR) {
for (int j = 0; j < list.value.size(); j++) {
cout << get<string>(list.value[j].value) << ", ";
}
}
else if (list.value[0].type == valtype::BOOL) {
for (int j = 0; j < list.value.size(); j++) {
cout << (get<bool>(list.value[j].value) ? "true" : "false") << ", ";
}
}
cout << endl;
} else {
syntaxError.fnTypeMismatch("printlist", {}, valtype::UNKNOWN);
}
break;
2025-05-10 21:28:56 +10:00
} 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;
case valtype::DEC:
exit(get<double>(nextToken.value.value));
break;
default:
vector<string> validTypes = {"int", "dec"};
syntaxError.fnTypeMismatch("exit", validTypes, nextToken.type);
}
}
} else if (tokens[i].keyword == keywords::LET) {
i++;
if (tokens.size() <= i + 2) {
syntaxError.fnNotSufficientArgs("let", 3, 3, tokens.size() - i);
break;
}
Token typeToken = tokens[i];
Token nameToken = tokens[i + 1];
Token valueToken = tokens[i + 2];
i += 2;
// Validate that we have a valid variable name
if (nameToken.type != valtype::STR) {
vector<string> validTypes = {"str"};
syntaxError.fnTypeMismatch("let (variable name)", validTypes, nameToken.type);
continue;
}
string varName = get<string>(nameToken.value.value);
Value newValue;
2025-05-13 14:05:29 +10:00
if (valueToken.keyword == keywords::LISTOBJ) {
if (typeToken.value.type == get<List>(valueToken.value.value).type) {
newValue.type = valtype::LIST;
newValue.value = get<List>(valueToken.value.value);
} else {
syntaxError.fnTypeMismatch("let (listobj)", {log.getTypeString(get<List>(valueToken.value.value).type)}, valueToken.type, "Variable name is " + varName);
}
}
2025-05-10 21:28:56 +10:00
// Check the type declaration matches the value
2025-05-13 14:05:29 +10:00
else if (typeToken.keyword == keywords::INT && valueToken.type == valtype::INT) {
2025-05-10 21:28:56 +10:00
newValue.type = valtype::INT;
newValue.value = get<int>(valueToken.value.value);
}
else if (typeToken.keyword == keywords::DEC && valueToken.type == valtype::DEC) {
newValue.type = valtype::DEC;
newValue.value = get<double>(valueToken.value.value);
}
else if (typeToken.keyword == keywords::STR && valueToken.type == valtype::STR) {
newValue.type = valtype::STR;
newValue.value = get<string>(valueToken.value.value);
}
else if (typeToken.keyword == keywords::BOOL && valueToken.type == valtype::BOOL) {
newValue.type = valtype::BOOL;
newValue.value = get<bool>(valueToken.value.value);
}
else {
vector<string> validTypes;
if (typeToken.keyword == keywords::INT) validTypes = {"int"};
else if (typeToken.keyword == keywords::DEC) validTypes = {"dec"};
else if (typeToken.keyword == keywords::STR) validTypes = {"str"};
else if (typeToken.keyword == keywords::BOOL) validTypes = {"bool"};
syntaxError.fnTypeMismatch("let", validTypes, valueToken.type, "Variable name is " + varName);
continue;
}
// Store the variable
variables[varName] = newValue;
} else if (tokens[i].keyword == keywords::IF) {
i++;
if (tokens.size() < i) syntaxError.fnNotSufficientArgs("if", 1, 1, 0);
log.debug("IF statement token type: " + to_string(static_cast<int>(tokens[i].type)));
log.debug("IF statement token keyword: " + to_string(static_cast<int>(tokens[i].keyword)));
if (tokens[i].keyword != keywords::VALUE) syntaxError.generalError("if needs a value, not a keyword");
if (tokens[i].type != valtype::BOOL) syntaxError.comparisonTypeError("in an if statement");
if (!get<bool>(tokens[i].value.value)) {
skipScope ++;
2025-05-20 15:03:08 +10:00
return {};
2025-05-10 21:28:56 +10:00
}
} else if (tokens[i].keyword == keywords::WHILE) {
i++;
if (tokens.size() < i) syntaxError.fnNotSufficientArgs("while", 1, 1, 0);
if (tokens[i].keyword != keywords::VALUE) syntaxError.generalError("while needs a value, not a keyword");
if (tokens[i].type != valtype::BOOL) syntaxError.comparisonTypeError("in a while statement");
log.debug("This is a while loop");
if (get<bool>(tokens[i].value.value) == true) {
loop = tokenIndex - 2 - lengthOfLine;
repeatScope ++;
log.debug("While loop condition is true, will skip back to " + to_string(loop));
2025-05-11 13:55:52 +10:00
} else if (!get<bool>(tokens[i].value.value)) {
int braceLevel = 0;
while (++i < tokens.size()) {
if (tokens[i].keyword == keywords::OBRAC) braceLevel ++;
else if (tokens[i].keyword == keywords::CBRAC) {
if (braceLevel == 0) break;
braceLevel --;
}
break;
}
2025-05-10 21:28:56 +10:00
} else {
2025-05-11 13:55:52 +10:00
syntaxError.generalError("Achievement get: How did we get here?");
2025-05-10 21:28:56 +10:00
}
2025-05-18 14:27:04 +10:00
} else if (tokens[i].keyword == keywords::FUN) {
2025-05-16 06:17:57 +10:00
i++;
Function buf;
2025-05-20 15:03:08 +10:00
if (keywordToValtype(tokens[i].keyword) == valtype::KEYWORD) syntaxError.generalError("fun needs a defining value for arg 1, not a non-value.");
2025-05-16 06:17:57 +10:00
buf.type = keywordToValtype(tokens[i].keyword);
i++;
2025-05-20 15:03:08 +10:00
tokenIndex ++;
2025-05-16 06:17:57 +10:00
string valName;
if (tokens[i].value.type == valtype::STR) valName = get<string>(tokens[i].value.value);
2025-05-20 15:03:08 +10:00
functionnames.push_back(valName);
2025-05-16 06:17:57 +10:00
i++;
2025-05-20 15:03:08 +10:00
tokenIndex ++;
2025-05-18 14:27:04 +10:00
if (tokens[i].keyword != keywords::OPARE) syntaxError.generalError("This is a placeholder");
i++;
2025-05-20 15:03:08 +10:00
tokenIndex ++;
2025-05-18 14:27:04 +10:00
map<string, valtype> valuetypes;
2025-05-20 15:03:08 +10:00
// Get the arguments which we will use in the function
if (tokens[i + 1].keyword != keywords::CPARE) while (tokens[i].keyword != keywords::CPARE) {
2025-05-18 14:27:04 +10:00
// We should have a valtype, a name for the variable, and a comma if the thing hasn't ended already
if (keywordToValtype(tokens[i].keyword) == valtype::KEYWORD) syntaxError.generalError("Another placeholder");
valtype valbuf = keywordToValtype(tokens[i].keyword);
i++;
2025-05-20 15:03:08 +10:00
tokenIndex ++;
2025-05-18 14:27:04 +10:00
if (tokens[i].type != valtype::STR) syntaxError.generalError("A third placeholder");
2025-05-20 15:03:08 +10:00
log.debug("This function needs a " + log.getTypeString(valbuf) + " which is accessible as " + get<string>(tokens[i].value.value));
buf.argNames.push_back(get<string>(tokens[i].value.value));
2025-05-18 14:27:04 +10:00
valuetypes[get<string>(tokens[i].value.value)] = valbuf;
i++;
2025-05-20 15:03:08 +10:00
tokenIndex ++;
2025-05-18 14:27:04 +10:00
if (tokens[i].keyword == keywords::CPARE) break;
if (tokens[i].keyword != keywords::COMMA) syntaxError.generalError("Yet another placeholder");
i++;
2025-05-20 15:03:08 +10:00
tokenIndex ++;
2025-05-18 14:27:04 +10:00
}
2025-05-20 15:03:08 +10:00
else log.debug("Function does not require arguments");
tokenIndex ++;
buf.arguments = valuetypes;
buf.startToken = tokenIndex;
2025-05-18 14:27:04 +10:00
functions[valName] = buf;
2025-05-20 15:03:08 +10:00
log.debug("Init a function with name " + valName + " success. Function starts at index " + to_string(buf.startToken));
} else if (tokens[i].keyword == keywords::RETURN) {
i++;
if (i >= tokens.size()) syntaxError.fnNotSufficientArgs("return", 1, 1, 0);
if (isFunction){
return tokens[i++];
} else {
if (tokens[i].value.type != valtype::INT) syntaxError.improperReturnType("");
exit(get<int>(tokens[i].value.value));
}
2025-05-10 21:28:56 +10:00
} else {
if (tokens[i].keyword == keywords::VALUE && tokens[i].value.type == valtype::STR && variables.find(get<string>(tokens[i].value.value)) != variables.end()) {
log.debug("Manipulating variable...");
if (tokens.size() <= i + 2) { // Need at least 3 tokens: variable, operator, value
syntaxError.mathTooFewArgs();
2025-05-20 15:03:08 +10:00
return {};
2025-05-10 21:28:56 +10:00
}
string varName = get<string>(tokens[i].value.value);
i++;
if (tokens[i].keyword == keywords::SET) {
Token valueToken = tokens[i + 1];
if (valueToken.type != variables[varName].type) {
vector<string> validTypes;
switch(variables[varName].type) {
case valtype::INT: validTypes = {"int"}; break;
case valtype::DEC: validTypes = {"dec"}; break;
case valtype::STR: validTypes = {"str"}; break;
case valtype::BOOL: validTypes = {"bool"}; break;
2025-05-18 14:27:04 +10:00
default: validTypes = {"unknown"}; break;
2025-05-10 21:28:56 +10:00
}
syntaxError.fnTypeMismatch("assignment", validTypes, valueToken.type);
2025-05-20 15:03:08 +10:00
return {};
2025-05-10 21:28:56 +10:00
}
variables[varName].value = valueToken.value.value;
i++; // Skip the value token since we've processed it
} else if (tokens[i].keyword == keywords::INCREMENT) {
if (variables[varName].type == valtype::INT) {
variables[varName].value = get<int>(variables[varName].value) + 1;
} else {
syntaxError.mathCannotDoOperationOnType("++", "non-int");
}
} else if (tokens[i].keyword == keywords::DECREMENT) {
if (variables[varName].type == valtype::INT) {
variables[varName].value = get<int>(variables[varName].value) - 1;
} else {
syntaxError.mathCannotDoOperationOnType("--", "non-int");
}
} else if (tokens[i].keyword == keywords::ADDTO) {
if (tokens.size() < i + 1) syntaxError.mathTooFewArgs();
if (tokens[i + 1].value.type != variables[varName].type) syntaxError.mathTypeMismatch("Expected same type when adding");
if (variables[varName].type == valtype::INT) {
variables[varName].value = get<int>(variables[varName].value) + get<int>(tokens[i + 1].value.value);
} else if (variables[varName].type == valtype::DEC) {
variables[varName].value = get<double>(variables[varName].value) + get<double>(tokens[i + 1].value.value);
} else {
syntaxError.mathCannotDoOperationOnType("+=", "non-numeric");
}
i++;
} else {
syntaxError.mathCannotDoOperationOnType("unknown", "any");
}
} else {
if (tokens[i].keyword != keywords::CBRAC) syntaxError.unknownFn();
}
}
}
2025-05-20 15:03:08 +10:00
return {};
2025-05-10 21:28:56 +10:00
}