Basic math support

This commit is contained in:
2025-10-30 10:55:21 +00:00
parent 7c633c334c
commit 817cf5a1a9
6 changed files with 264 additions and 8 deletions

View File

@@ -83,6 +83,14 @@ Lexer::Lexer(std::string in) : file(std::move(in)) {
content.emplace_back("<"); content.emplace_back("<");
} }
break; break;
case '!':
if (peek() == '=') {
consume();
content.emplace_back("!=");
} else {
content.emplace_back("!");
}
break;
case '+': case '+':
case '-': case '-':
case '*': case '*':

View File

@@ -15,7 +15,7 @@
class Lexer { class Lexer {
private: private:
std::vector<char> delimiters = { std::vector<char> delimiters = {
'(', ')', '{', '}', '[', ']', '.', '\n', '+', '-', '*', '/', '^', '>', '<', ' ', ',' '(', ')', '{', '}', '[', ']', '.', '\n', '+', '-', '*', '/', '^', '>', '<', ' ', ',', '!'
}; };
std::string file; std::string file;
size_t incrementor = -1; size_t incrementor = -1;

View File

@@ -8,6 +8,82 @@
#include <string> #include <string>
#include <vector> #include <vector>
std::map<std::string, OperatorType> ASTOperator::operatorMap = {
{"==", OperatorType::Equal},
{"!=", OperatorType::Inequal},
{">=", OperatorType::GreaterEqual},
{"<=", OperatorType::LessEqual},
{">", OperatorType::Greater},
{"<", OperatorType::Less},
{"+", OperatorType::Add},
{"-", OperatorType::Subtract},
{"*", OperatorType::Multiply},
{"/", OperatorType::Divide},
{"+=", OperatorType::AddBy},
{"-=", OperatorType::SubtractBy},
{"*=", OperatorType::MultiplyBy},
{"/=", OperatorType::DivideBy}
};
ASTList::ASTList(std::vector<ASTNode> in) : elements(std::move(in)) {}
ASTOperator::ASTOperator(std::vector<std::string> args) {
std::vector<std::variant<ASTNode, OperatorType>> outputQueue;
std::vector<OperatorType> operatorStack;
auto getPrecedence = [&](OperatorType op) {
switch (op) {
case OperatorType::Multiply:
case OperatorType::Divide:
return 2;
case OperatorType::Add:
case OperatorType::Subtract:
return 1;
case OperatorType::Equal:
case OperatorType::Inequal:
case OperatorType::Less:
case OperatorType::LessEqual:
case OperatorType::Greater:
case OperatorType::GreaterEqual:
return 0;
default:
return -1;
}
};
for (const std::string& token : args) {
if (operatorMap.find(token) != operatorMap.end()) {
// it's an operator
OperatorType o1 = operatorMap[token];
while (!operatorStack.empty()) {
OperatorType o2 = operatorStack.back();
if (getPrecedence(o2) >= getPrecedence(o1)) {
arguments.emplace_back(o2);
operatorStack.pop_back();
} else {
break;
}
}
operatorStack.push_back(o1);
} else {
// it's an operand (Value or Identifier)
ASTCodeBlock tempBlock({token});
if (!tempBlock.nodes.empty()) {
arguments.emplace_back(tempBlock.nodes[0]);
} else {
// This should not happen if token is valid
std::cout << "Invalid token in expression: " << token << std::endl;
exit(1);
}
}
}
while (!operatorStack.empty()) {
arguments.emplace_back(operatorStack.back());
operatorStack.pop_back();
}
}
ASTValue::ASTValue() : type(ValueType::None) {} ASTValue::ASTValue() : type(ValueType::None) {}
ASTValue::ASTValue(std::string in) : type(ValueType::String), value(in) {} ASTValue::ASTValue(std::string in) : type(ValueType::String), value(in) {}
ASTValue::ASTValue(bool in) : type(ValueType::Bool), value(in) {} ASTValue::ASTValue(bool in) : type(ValueType::Bool), value(in) {}
@@ -15,8 +91,6 @@ ASTValue::ASTValue(long long in) : type(ValueType::Int), value(in) {}
ASTValue::ASTValue(double in) : type(ValueType::Float), value(in) {} ASTValue::ASTValue(double in) : type(ValueType::Float), value(in) {}
ASTValue::ASTValue(ASTList in) : type(ValueType::List), value(std::move(in)) {} ASTValue::ASTValue(ASTList in) : type(ValueType::List), value(std::move(in)) {}
ASTList::ASTList(std::vector<ASTNode> in) : elements(std::move(in)) {}
ValueType ASTValue::getValueType(std::string in) { ValueType ASTValue::getValueType(std::string in) {
if (in.size() < 1) { if (in.size() < 1) {
return ValueType::None; return ValueType::None;
@@ -53,7 +127,7 @@ ValueType ASTValue::getValueType(std::string in) {
} }
ASTFunction::ASTFunction(ASTCodeBlock body) : body(std::move(body)) {} ASTFunction::ASTFunction(ASTCodeBlock body) : body(std::move(body)) {}
ASTFunction::ASTFunction() {} ASTFunction::ASTFunction() = default;
ASTFunctionCall::ASTFunctionCall(std::string func, std::vector<ASTNode> args) : func(std::move(func)), args(std::move(args)) {} ASTFunctionCall::ASTFunctionCall(std::string func, std::vector<ASTNode> args) : func(std::move(func)), args(std::move(args)) {}
ASTIdentifier::ASTIdentifier(std::string in) : name(std::move(in)) {} ASTIdentifier::ASTIdentifier(std::string in) : name(std::move(in)) {}
@@ -98,6 +172,10 @@ std::optional<ASTList> ASTValue::getList() const {
} }
} }
std::vector<std::variant<ASTNode, OperatorType>> ASTOperator::getArguments() const {
return arguments;
}
std::vector<ASTNode> ASTList::getElements() const { std::vector<ASTNode> ASTList::getElements() const {
return elements; return elements;
} }
@@ -128,6 +206,10 @@ TokenType ASTCodeBlock::getTokenType() {
} else { } else {
return TokenType::None; return TokenType::None;
} }
if (ASTOperator::operatorMap.find(token) != ASTOperator::operatorMap.end()) {
return TokenType::Operator;
}
// Check for values first // Check for values first
if (ASTValue().getValueType(token) != ValueType::None) { if (ASTValue().getValueType(token) != ValueType::None) {
@@ -199,6 +281,26 @@ void ASTCodeBlock::parseBlock() {
return; return;
} }
TokenType tokenType = getTokenType(); TokenType tokenType = getTokenType();
// Expression parsing logic
if (tokenType == TokenType::Value || tokenType == TokenType::Identifier) {
if (peek(1).has_value() && ASTOperator::operatorMap.count(peek(1).value())) {
std::vector<std::string> expressionTokens;
while (peek(0).has_value()) {
TokenType currentTokenType = getTokenType(); // use a different variable
if (currentTokenType == TokenType::Value || currentTokenType == TokenType::Identifier || currentTokenType == TokenType::Operator) {
expressionTokens.push_back(consume().value());
} else {
break;
}
}
if (!expressionTokens.empty()) {
nodes.emplace_back(std::make_shared<ASTOperator>(expressionTokens));
}
continue;
}
}
ValueType valueType = ASTValue().getValueType(token.value()); ValueType valueType = ASTValue().getValueType(token.value());
std::optional<std::string> currentToken = consume(); std::optional<std::string> currentToken = consume();

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <map>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <vector> #include <vector>
@@ -11,8 +12,9 @@ class ASTFunction;
class ASTFunctionCall; class ASTFunctionCall;
class ASTCodeBlock; class ASTCodeBlock;
class ASTIdentifier; class ASTIdentifier;
class ASTOperator;
typedef std::variant<std::shared_ptr<ASTValue>, std::shared_ptr<ASTFunction>, std::shared_ptr<ASTFunctionCall>, std::shared_ptr<ASTCodeBlock>, std::shared_ptr<ASTIdentifier>> ASTNode; typedef std::variant<std::shared_ptr<ASTValue>, std::shared_ptr<ASTFunction>, std::shared_ptr<ASTFunctionCall>, std::shared_ptr<ASTCodeBlock>, std::shared_ptr<ASTIdentifier>, std::shared_ptr<ASTOperator>> ASTNode;
class ASTList { class ASTList {
private: private:
@@ -30,7 +32,20 @@ enum class ValueType {
}; };
enum class TokenType { enum class TokenType {
Identifier, Value, Function, FunctionCallStart, OpenParen, CloseParen, CodeBlockStart, CodeBlockEnd, NewLine, ListStart, ListEnd, None Identifier, Value, Function, FunctionCallStart, OpenParen, CloseParen, CodeBlockStart, CodeBlockEnd, NewLine, ListStart, ListEnd, Operator, None
};
enum class OperatorType {
Add, Subtract, Multiply, Divide, Equal, Inequal, Less, LessEqual, Greater, GreaterEqual, AddBy, MultiplyBy, DivideBy, SubtractBy
};
class ASTOperator {
private:
std::vector<std::variant<ASTNode, OperatorType>> arguments;
public:
static std::map<std::string, OperatorType> operatorMap;
explicit ASTOperator(std::vector<std::string> args);
std::vector<std::variant<ASTNode, OperatorType>> getArguments() const;
}; };
/** /**

View File

@@ -82,6 +82,116 @@ void Executor::printValue(const ASTValue& arg) {
} }
} }
ASTValue Executor::evaluateOperator(const std::shared_ptr<ASTOperator>& op) {
std::vector<ASTValue> stack;
auto args = op->getArguments();
for (const auto& arg : args) {
if (std::holds_alternative<ASTNode>(arg)) {
ASTNode node = std::get<ASTNode>(arg);
if (std::holds_alternative<std::shared_ptr<ASTValue>>(node)) {
stack.push_back(*std::get<std::shared_ptr<ASTValue>>(node));
} else if (std::holds_alternative<std::shared_ptr<ASTIdentifier>>(node)) {
std::string varName = std::get<std::shared_ptr<ASTIdentifier>>(node)->name;
if (variables.count(varName)) {
Object obj = variables[varName];
if (!obj.isCustomObject && std::holds_alternative<ASTValue>(obj.value)) {
stack.push_back(std::get<ASTValue>(obj.value));
} else {
std::cout << "Variable " << varName << " is not a value in expression" << std::endl;
exit(1);
}
} else {
std::cout << "Undefined variable " << varName << " in expression" << std::endl;
exit(1);
}
}
} else { // It's an OperatorType
OperatorType opType = std::get<OperatorType>(arg);
if (stack.size() < 2) {
std::cout << "Syntax error in expression: not enough operands for operator" << std::endl;
exit(1);
}
ASTValue right = stack.back();
stack.pop_back();
ASTValue left = stack.back();
stack.pop_back();
if ((left.type == ValueType::Int || left.type == ValueType::Float) &&
(right.type == ValueType::Int || right.type == ValueType::Float)) {
double l = left.type == ValueType::Int ? (double)left.getInt().value() : left.getFloat().value();
double r = right.type == ValueType::Int ? (double)right.getInt().value() : right.getFloat().value();
bool resultIsFloat = left.type == ValueType::Float || right.type == ValueType::Float;
switch (opType) {
case OperatorType::Add:
resultIsFloat ? stack.emplace_back(l + r) : stack.emplace_back((long long)(l + r));
break;
case OperatorType::Subtract:
resultIsFloat ? stack.emplace_back(l - r) : stack.emplace_back((long long)(l - r));
break;
case OperatorType::Multiply:
resultIsFloat ? stack.emplace_back(l * r) : stack.emplace_back((long long)(l * r));
break;
case OperatorType::Divide:
if (r == 0) { std::cout << "Division by zero" << std::endl; exit(1); }
stack.emplace_back(l / r);
break;
case OperatorType::Equal:
stack.emplace_back(l == r);
break;
case OperatorType::Inequal:
stack.emplace_back(l != r);
break;
case OperatorType::Greater:
stack.emplace_back(l > r);
break;
case OperatorType::GreaterEqual:
stack.emplace_back(l >= r);
break;
case OperatorType::Less:
stack.emplace_back(l < r);
break;
case OperatorType::LessEqual:
stack.emplace_back(l <= r);
break;
default:
std::cout << "Unsupported operator for numeric types" << std::endl;
exit(1);
}
} else if (left.type == ValueType::String && opType == OperatorType::Add) {
std::string r_str;
if (right.type == ValueType::String) r_str = right.getString().value();
else if (right.type == ValueType::Int) r_str = std::to_string(right.getInt().value());
else if (right.type == ValueType::Float) r_str = std::to_string(right.getFloat().value());
else if (right.type == ValueType::Bool) r_str = right.getBool().value() ? "true" : "false";
else { std::cout << "Unsupported type for string concatenation" << std::endl; exit(1); }
stack.emplace_back(left.getString().value() + r_str);
} else if (left.type == ValueType::Bool && right.type == ValueType::Bool) {
bool l = left.getBool().value();
bool r = right.getBool().value();
switch (opType) {
case OperatorType::Equal: stack.emplace_back(l == r); break;
case OperatorType::Inequal: stack.emplace_back(l != r); break;
default: std::cout << "Unsupported operator for booleans" << std::endl; exit(1);
}
}
else {
std::cout << "Unsupported operand types in expression" << std::endl;
exit(1);
}
}
}
if (stack.size() != 1) {
std::cout << "Invalid expression" << std::endl;
exit(1);
}
return stack.back();
}
std::optional<ASTNode> Executor::consume() { std::optional<ASTNode> Executor::consume() {
if (iterator < code.nodes.size()) { if (iterator < code.nodes.size()) {
return code.nodes[iterator++]; return code.nodes[iterator++];
@@ -141,6 +251,10 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, Objec
if (node.has_value()) { if (node.has_value()) {
// for if we see an identifier // for if we see an identifier
if (std::holds_alternative<std::shared_ptr<ASTIdentifier>>(node.value())) { if (std::holds_alternative<std::shared_ptr<ASTIdentifier>>(node.value())) {
if (std::holds_alternative<std::shared_ptr<ASTOperator>>(node.value())) {
evaluateOperator(std::get<std::shared_ptr<ASTOperator>>(node.value()));
continue;
}
std::optional<ASTNode> next = consume(); std::optional<ASTNode> next = consume();
if (next.has_value()) { if (next.has_value()) {
// function assignment // function assignment
@@ -159,6 +273,9 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, Objec
variables.emplace(std::get<std::shared_ptr<ASTIdentifier>>(node.value())->name, *std::get<std::shared_ptr<ASTValue>>(valueNode.value())); variables.emplace(std::get<std::shared_ptr<ASTIdentifier>>(node.value())->name, *std::get<std::shared_ptr<ASTValue>>(valueNode.value()));
} else if (std::holds_alternative<std::shared_ptr<ASTFunction>>(valueNode.value())) { } else if (std::holds_alternative<std::shared_ptr<ASTFunction>>(valueNode.value())) {
variables.emplace(std::get<std::shared_ptr<ASTIdentifier>>(node.value())->name, *std::get<std::shared_ptr<ASTFunction>>(valueNode.value())); variables.emplace(std::get<std::shared_ptr<ASTIdentifier>>(node.value())->name, *std::get<std::shared_ptr<ASTFunction>>(valueNode.value()));
} else if (std::holds_alternative<std::shared_ptr<ASTOperator>>(valueNode.value())) {
ASTValue result = evaluateOperator(std::get<std::shared_ptr<ASTOperator>>(valueNode.value()));
variables.emplace(std::get<std::shared_ptr<ASTIdentifier>>(node.value())->name, result);
} else { } else {
std::cout << "Expected value or function after = sign" << std::endl; std::cout << "Expected value or function after = sign" << std::endl;
exit(1); exit(1);
@@ -188,6 +305,9 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, Objec
if (variables.find(std::get<std::shared_ptr<ASTIdentifier>>(callArgNode)->name) != variables.end()) { if (variables.find(std::get<std::shared_ptr<ASTIdentifier>>(callArgNode)->name) != variables.end()) {
callArgs.push_back(variables[std::get<std::shared_ptr<ASTIdentifier>>(callArgNode)->name]); callArgs.push_back(variables[std::get<std::shared_ptr<ASTIdentifier>>(callArgNode)->name]);
} }
} else if (std::holds_alternative<std::shared_ptr<ASTOperator>>(callArgNode)) {
ASTValue result = evaluateOperator(std::get<std::shared_ptr<ASTOperator>>(callArgNode));
callArgs.emplace_back(result);
} }
} }
@@ -288,7 +408,12 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, Objec
} }
if (arg.getBool().value()) { if (arg.getBool().value()) {
if (std::holds_alternative<std::shared_ptr<ASTCodeBlock>>(block.value())) { if (std::holds_alternative<std::shared_ptr<ASTCodeBlock>>(block.value())) {
Executor(*std::get<std::shared_ptr<ASTCodeBlock>>(block.value()), false, variables); Executor newExecutor(*std::get<std::shared_ptr<ASTCodeBlock>>(block.value()), false, variables);
for (const auto& [key, value] : newExecutor.getVariables()) {
if (variables.find(key) != variables.end()) {
variables[key] = newExecutor.getVariables()[key];
}
}
} }
} }
} else if (fnName == "while") { } else if (fnName == "while") {
@@ -315,7 +440,12 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, Objec
if (arg.getBool().value()) { if (arg.getBool().value()) {
while (arg.getBool().value()) { while (arg.getBool().value()) {
if (std::holds_alternative<std::shared_ptr<ASTCodeBlock>>(block.value())) { if (std::holds_alternative<std::shared_ptr<ASTCodeBlock>>(block.value())) {
Executor(*std::get<std::shared_ptr<ASTCodeBlock>>(block.value()), false, variables); Executor newExecutor(*std::get<std::shared_ptr<ASTCodeBlock>>(block.value()), false, variables);
for (const auto& [key, value] : newExecutor.getVariables()) {
if (variables.find(key) != variables.end()) {
variables[key] = newExecutor.getVariables()[key];
}
}
} }
} }
} }

View File

@@ -36,6 +36,7 @@ class Executor {
std::optional<ASTNode> consume(); std::optional<ASTNode> consume();
std::optional<ASTNode> peek(int ahead = 1); std::optional<ASTNode> peek(int ahead = 1);
void printValue(const ASTValue& arg); void printValue(const ASTValue& arg);
ASTValue evaluateOperator(const std::shared_ptr<ASTOperator>& op);
public: public:
explicit Executor(ASTCodeBlock in, bool isInitCall = false, std::map<std::string, Object> scopeVals = {}, std::vector<Object> args = {}); explicit Executor(ASTCodeBlock in, bool isInitCall = false, std::map<std::string, Object> scopeVals = {}, std::vector<Object> args = {});
std::map<std::string, Object> getVariables(); std::map<std::string, Object> getVariables();