Basic math support
This commit is contained in:
@@ -83,6 +83,14 @@ Lexer::Lexer(std::string in) : file(std::move(in)) {
|
||||
content.emplace_back("<");
|
||||
}
|
||||
break;
|
||||
case '!':
|
||||
if (peek() == '=') {
|
||||
consume();
|
||||
content.emplace_back("!=");
|
||||
} else {
|
||||
content.emplace_back("!");
|
||||
}
|
||||
break;
|
||||
case '+':
|
||||
case '-':
|
||||
case '*':
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
class Lexer {
|
||||
private:
|
||||
std::vector<char> delimiters = {
|
||||
'(', ')', '{', '}', '[', ']', '.', '\n', '+', '-', '*', '/', '^', '>', '<', ' ', ','
|
||||
'(', ')', '{', '}', '[', ']', '.', '\n', '+', '-', '*', '/', '^', '>', '<', ' ', ',', '!'
|
||||
};
|
||||
std::string file;
|
||||
size_t incrementor = -1;
|
||||
|
||||
@@ -8,6 +8,82 @@
|
||||
#include <string>
|
||||
#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(std::string in) : type(ValueType::String), 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(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) {
|
||||
if (in.size() < 1) {
|
||||
return ValueType::None;
|
||||
@@ -53,7 +127,7 @@ ValueType ASTValue::getValueType(std::string in) {
|
||||
}
|
||||
|
||||
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)) {}
|
||||
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 {
|
||||
return elements;
|
||||
}
|
||||
@@ -129,6 +207,10 @@ TokenType ASTCodeBlock::getTokenType() {
|
||||
return TokenType::None;
|
||||
}
|
||||
|
||||
if (ASTOperator::operatorMap.find(token) != ASTOperator::operatorMap.end()) {
|
||||
return TokenType::Operator;
|
||||
}
|
||||
|
||||
// Check for values first
|
||||
if (ASTValue().getValueType(token) != ValueType::None) {
|
||||
return TokenType::Value;
|
||||
@@ -199,6 +281,26 @@ void ASTCodeBlock::parseBlock() {
|
||||
return;
|
||||
}
|
||||
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());
|
||||
std::optional<std::string> currentToken = consume();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
@@ -11,8 +12,9 @@ class ASTFunction;
|
||||
class ASTFunctionCall;
|
||||
class ASTCodeBlock;
|
||||
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 {
|
||||
private:
|
||||
@@ -30,7 +32,20 @@ enum class ValueType {
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() {
|
||||
if (iterator < code.nodes.size()) {
|
||||
return code.nodes[iterator++];
|
||||
@@ -141,6 +251,10 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, Objec
|
||||
if (node.has_value()) {
|
||||
// for if we see an identifier
|
||||
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();
|
||||
if (next.has_value()) {
|
||||
// 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()));
|
||||
} 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()));
|
||||
} 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 {
|
||||
std::cout << "Expected value or function after = sign" << std::endl;
|
||||
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()) {
|
||||
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 (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") {
|
||||
@@ -315,7 +440,12 @@ Executor::Executor(ASTCodeBlock in, bool isInitCall, std::map<std::string, Objec
|
||||
if (arg.getBool().value()) {
|
||||
while (arg.getBool().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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ class Executor {
|
||||
std::optional<ASTNode> consume();
|
||||
std::optional<ASTNode> peek(int ahead = 1);
|
||||
void printValue(const ASTValue& arg);
|
||||
ASTValue evaluateOperator(const std::shared_ptr<ASTOperator>& op);
|
||||
public:
|
||||
explicit Executor(ASTCodeBlock in, bool isInitCall = false, std::map<std::string, Object> scopeVals = {}, std::vector<Object> args = {});
|
||||
std::map<std::string, Object> getVariables();
|
||||
|
||||
Reference in New Issue
Block a user