From 708b3f14abca7a83e4c8543abea910a90c4a647e Mon Sep 17 00:00:00 2001 From: Samathingamajig Date: Sun, 21 Feb 2021 01:12:07 -0600 Subject: [PATCH] Added `Boolean` and `Null` type and Global Constant Variables Also added better error handling in SymbolTable::set() Also fixed `warning C4715: 'Interpreter::visit': not all control paths return a value` --- BarkScript.cpp | 2 +- interpreter/Interpreter.cpp | 14 ++- object/Object.cpp | 219 ++++++++++++++++++++++++++++++++++-- object/object.h | 39 +++++++ symboltable/SymbolTable.cpp | 25 +++- symboltable/symboltable.h | 13 ++- 6 files changed, 293 insertions(+), 19 deletions(-) diff --git a/BarkScript.cpp b/BarkScript.cpp index 4593392..5430c5d 100644 --- a/BarkScript.cpp +++ b/BarkScript.cpp @@ -8,7 +8,7 @@ #include "interpreter/interpreter.h" #include "context/context.h" -const std::string bsversion = "0.1.1"; +const std::string bsversion = "0.1.2"; int main() { std::cout << "BarkScript version " << bsversion << std::endl; diff --git a/interpreter/Interpreter.cpp b/interpreter/Interpreter.cpp index bd2a7d9..e655df1 100644 --- a/interpreter/Interpreter.cpp +++ b/interpreter/Interpreter.cpp @@ -42,7 +42,7 @@ RuntimeResult Interpreter::visit(spNode node, spContext context) { } else if (type == nodetypes::UnaryOperator) { return visitUnaryOperatorNode(node, context); } else { - std::cout << "Error: node type " + type + " does not have a visit method!" << std::endl; + return RuntimeResult().failure(makeSharedError(RuntimeError(node->positionStart, node->positionEnd, "Error: node type " + type + " does not have a visit method!", context))); } } @@ -58,10 +58,14 @@ RuntimeResult Interpreter::visitVariableAssignmentNode(spNode node, spContext co std::string variableName = node->token.value; spObject value = rt.registerRT(visit(node->valueNode, context)); if (rt.hasError()) return rt; - bool success = context->symbolTable->set(variableName, value, true); - if (!success) - return rt.failure(makeSharedError(RuntimeError(node->token.positionStart, node->positionEnd, "Variable not reassigned properly", context))); - return rt.success(value); + SymbolTableSetReturnCode success = context->symbolTable->set(variableName, value, true); + switch (success) { + case SymbolTableSetReturnCode::perfect: { return rt.success(value); } + case SymbolTableSetReturnCode::errorGlobalConstantVariable: { return rt.failure(makeSharedError(RuntimeError(node->token.positionStart, node->positionEnd, "You cannot modify a global constant variable!", context))); } + case SymbolTableSetReturnCode::errorUserDefinedConstantVariable: { return rt.failure(makeSharedError(RuntimeError(node->token.positionStart, node->positionEnd, "You cannot modify a constant variable!", context))); } + case SymbolTableSetReturnCode::errorNotInScope: { return rt.failure(makeSharedError(RuntimeError(node->token.positionStart, node->positionEnd, "Variable " + variableName + " does not exist in the current scope!", context))); } + default: { return rt.failure(makeSharedError(RuntimeError(node->token.positionStart, node->positionEnd, "Unknown return value when setting: " + std::to_string((int) success), context))); } + } } RuntimeResult Interpreter::visitVariableRetrievementNode(spNode node, spContext context) { diff --git a/object/Object.cpp b/object/Object.cpp index 1096e31..7240430 100644 --- a/object/Object.cpp +++ b/object/Object.cpp @@ -12,8 +12,9 @@ bool didUnderflow(double value) { } template -RuntimeResult notImplemented(RuntimeResult rt, T self, spObject other, std::string function) { - return rt.failure(makeSharedError(RuntimeError(self->positionStart, other->positionEnd, function + " is not implemented between " + self->type + " and " + other->type + "!", self->context))); +RuntimeResult notImplemented(RuntimeResult rt, T self, spObject other, std::string function, std::string extra = "") { + extra = extra.size() > 0 ? " (" + extra + ")" : ""; + return rt.failure(makeSharedError(RuntimeError(self->positionStart, other->positionEnd, function + " is not implemented between " + self->type + " and " + other->type + "!" + extra, self->context))); } void Object::setPosition(Position positionStart, Position positionEnd) { @@ -25,6 +26,24 @@ void Object::setContext(spContext context) { this->context = context; } +RuntimeResult Object::toOther(spObject other) { + std::string type = other->type; + + if (type == "Number") { + return this->toNumber(); + } else if (type == "Boolean") { + return this->toBoolean(); + } else if (type == "Null") { + return this->toNull(); + } else { + return RuntimeResult().failure(makeSharedError(RuntimeError(this->positionStart, this->positionEnd, "No conversion from " + this->type + " to " + other->type + " exists!", this->context))); + } +} + +RuntimeResult Object::toNull() { + return RuntimeResult().success(makeSharedObject(Null())); +} + Number::Number(double value, bool sign) { this->type = "Number"; this->doubleValue = value; @@ -82,7 +101,14 @@ spObject Number::copy() { RuntimeResult Number::binary_plus(spObject other) { RuntimeResult rt; - if (other->type != objecttypes::Number) return notImplemented(rt, this, other, "binary_plus"); + if (other->type != objecttypes::Number) { + spObject tempOther = rt.registerRT(other->toNumber()); + if (rt.hasError()) { + return notImplemented(rt, this, other, "binary_plus", rt.error->details); + } else { + other = tempOther; + } + } if (this->isNaN || other->isNaN) return rt.success(makeSharedObject(Number("NaN"))); if (this->isInfinity || other->isInfinity) { @@ -103,7 +129,14 @@ RuntimeResult Number::binary_plus(spObject other) { RuntimeResult Number::binary_minus(spObject other) { RuntimeResult rt; - if (other->type != objecttypes::Number) return notImplemented(rt, this, other, "binary_minus"); + if (other->type != objecttypes::Number) { + spObject tempOther = rt.registerRT(other->toNumber()); + if (rt.hasError()) { + return notImplemented(rt, this, other, "binary_minus", rt.error->details); + } else { + other = tempOther; + } + } if (this->isNaN || other->isNaN) return rt.success(makeSharedObject(Number("NaN"))); if (this->isInfinity || other->isInfinity) { @@ -124,7 +157,14 @@ RuntimeResult Number::binary_minus(spObject other) { RuntimeResult Number::binary_asterisk(spObject other) { RuntimeResult rt; - if (other->type != objecttypes::Number) return notImplemented(rt, this, other, "binary_asterisk"); + if (other->type != objecttypes::Number) { + spObject tempOther = rt.registerRT(other->toNumber()); + if (rt.hasError()) { + return notImplemented(rt, this, other, "binary_asterisk", rt.error->details); + } else { + other = tempOther; + } + } if (this->isNaN || other->isNaN) return rt.success(makeSharedObject(Number("NaN"))); if (this->isInfinity || other->isInfinity) { @@ -146,7 +186,14 @@ RuntimeResult Number::binary_asterisk(spObject other) { RuntimeResult Number::binary_f_slash(spObject other) { RuntimeResult rt; - if (other->type != objecttypes::Number) return notImplemented(rt, this, other, "binary_f_slash"); + if (other->type != objecttypes::Number) { + spObject tempOther = rt.registerRT(other->toNumber()); + if (rt.hasError()) { + return notImplemented(rt, this, other, "binary_f_slash", rt.error->details); + } else { + other = tempOther; + } + } if (other->isPureZero) return rt.failure(makeSharedError(RuntimeError(other->positionStart, other->positionEnd, "Division by 0", this->context))); if (this->isNaN || other->isNaN) return rt.success(makeSharedObject(Number("NaN"))); @@ -162,7 +209,14 @@ RuntimeResult Number::binary_f_slash(spObject other) { RuntimeResult Number::binary_double_asterisk(spObject other) { RuntimeResult rt; - if (other->type != objecttypes::Number) return notImplemented(rt, this, other, "binary_double_asterisk"); + if (other->type != objecttypes::Number) { + spObject tempOther = rt.registerRT(other->toNumber()); + if (rt.hasError()) { + return notImplemented(rt, this, other, "binary_double_asterisk", rt.error->details); + } else { + other = tempOther; + } + } if (this->isNaN || other->isNaN) return rt.success(makeSharedObject(Number("NaN"))); if (other->isPureZero) return rt.success(makeSharedObject(Number(1))); @@ -179,7 +233,14 @@ RuntimeResult Number::binary_double_asterisk(spObject other) { RuntimeResult Number::binary_double_f_slash(spObject other) { RuntimeResult rt; - if (other->type != objecttypes::Number) return notImplemented(rt, this, other, "binary_double_f_slash"); + if (other->type != objecttypes::Number) { + spObject tempOther = rt.registerRT(other->toNumber()); + if (rt.hasError()) { + return notImplemented(rt, this, other, "binary_double_f_slash", rt.error->details); + } else { + other = tempOther; + } + } if (other->isPureZero) return rt.failure(makeSharedError(RuntimeError(other->positionStart, other->positionEnd, "Floored division by 0", this->context))); spObject normalDivisionResult = rt.registerRT(this->binary_f_slash(other)); @@ -206,3 +267,145 @@ RuntimeResult Number::unary_minus() { else if (isInfinity) return rt.success(makeSharedObject(Number("Infinity", !sign))); return rt.success(makeSharedObject(Number(this->doubleValue * -1))); } + +RuntimeResult Number::toNumber() { + return RuntimeResult().success(makeSharedObject(*this)); +} + +RuntimeResult Number::toBoolean() { + return RuntimeResult().success(makeSharedObject(Boolean(!(this->isPureZero || this->isNaN)))); +} + +Boolean::Boolean(bool value) { + this->type = "Boolean"; + this->isPureDouble = true; + this->isPureZero = !value; + this->doubleValue = value; +} + +std::string Boolean::to_string() { + return this->isPureZero ? "false" : "true"; +} + +spObject Boolean::copy() { + return makeSharedObject(Boolean(this->doubleValue)); +} + +RuntimeResult Boolean::toNumber() { + return RuntimeResult().success(makeSharedObject(Number(this->doubleValue))); +} + +RuntimeResult Boolean::toBoolean() { + return RuntimeResult().success(makeSharedObject(*this)); +} + +std::string Null::to_string() { + return "null"; +} + +spObject Null::copy() { + return makeSharedObject(Null()); +} + +RuntimeResult Null::binary_plus(spObject other) { + RuntimeResult rt; + if (other->type == "Null") { + spObject self = rt.registerRT(this->toNumber()); + if (rt.hasError()) return rt; + spObject tempOther = rt.registerRT(this->toNumber()); + if (rt.hasError()) return rt; + return self->binary_plus(tempOther); + } + spObject self = rt.registerRT(this->toOther(other)); + if (rt.hasError()) return notImplemented(rt, this, other, "binary_plus", rt.error->details); + return self->binary_plus(other); +} + +RuntimeResult Null::binary_minus(spObject other) { + RuntimeResult rt; + if (other->type == "Null") { + spObject self = rt.registerRT(this->toNumber()); + if (rt.hasError()) return rt; + spObject tempOther = rt.registerRT(this->toNumber()); + if (rt.hasError()) return rt; + return self->binary_minus(tempOther); + } + spObject self = rt.registerRT(this->toOther(other)); + if (rt.hasError()) return notImplemented(rt, this, other, "binary_minus", rt.error->details); + return self->binary_minus(other); +} + +RuntimeResult Null::binary_asterisk(spObject other) { + RuntimeResult rt; + if (other->type == "Null") { + spObject self = rt.registerRT(this->toNumber()); + if (rt.hasError()) return rt; + spObject tempOther = rt.registerRT(this->toNumber()); + if (rt.hasError()) return rt; + return self->binary_asterisk(tempOther); + } + spObject self = rt.registerRT(this->toOther(other)); + if (rt.hasError()) return notImplemented(rt, this, other, "binary_asterisk", rt.error->details); + return self->binary_asterisk(other); +} + +RuntimeResult Null::binary_f_slash(spObject other) { + RuntimeResult rt; + if (other->type == "Null") { + spObject self = rt.registerRT(this->toNumber()); + if (rt.hasError()) return rt; + spObject tempOther = rt.registerRT(this->toNumber()); + if (rt.hasError()) return rt; + return self->binary_f_slash(tempOther); + } + spObject self = rt.registerRT(this->toOther(other)); + if (rt.hasError()) return notImplemented(rt, this, other, "binary_f_slash", rt.error->details); + return self->binary_f_slash(other); +} + +RuntimeResult Null::binary_double_asterisk(spObject other) { + RuntimeResult rt; + if (other->type == "Null") { + spObject self = rt.registerRT(this->toNumber()); + if (rt.hasError()) return rt; + spObject tempOther = rt.registerRT(this->toNumber()); + if (rt.hasError()) return rt; + return self->binary_double_asterisk(tempOther); + } + spObject self = rt.registerRT(this->toOther(other)); + if (rt.hasError()) return notImplemented(rt, this, other, "binary_double_asterisk", rt.error->details); + return self->binary_double_asterisk(other); +} + +RuntimeResult Null::binary_double_f_slash(spObject other) { + RuntimeResult rt; + if (other->type == "Null") { + spObject self = rt.registerRT(this->toNumber()); + if (rt.hasError()) return rt; + spObject tempOther = rt.registerRT(this->toNumber()); + if (rt.hasError()) return rt; + return self->binary_double_f_slash(tempOther); + } + spObject self = rt.registerRT(this->toOther(other)); + if (rt.hasError()) return notImplemented(rt, this, other, "binary_double_f_slash", rt.error->details); + return self->binary_double_f_slash(other); +} + +RuntimeResult Null::unary_plus() { + return this->toNumber(); +} + +RuntimeResult Null::unary_minus() { + RuntimeResult rt; + spObject self = rt.registerRT(this->toNumber()); + if (rt.hasError()) return rt; + return self->unary_minus(); +} + +RuntimeResult Null::toNumber() { + return RuntimeResult().success(makeSharedObject(Number(0))); +} + +RuntimeResult Null::toBoolean() { + return RuntimeResult().success(makeSharedObject(Boolean(false))); +} diff --git a/object/object.h b/object/object.h index 77a7b1a..8706443 100644 --- a/object/object.h +++ b/object/object.h @@ -57,6 +57,11 @@ struct Object { RuntimeResult virtual unary_plus() { return RuntimeResult().failure(makeSharedError(RuntimeError(this->positionStart, this->positionEnd, "unary_plus for " + this->type + " is not implemented!", this->context))); }; RuntimeResult virtual unary_minus() { return RuntimeResult().failure(makeSharedError(RuntimeError(this->positionStart, this->positionEnd, "unary_minus for " + this->type + " is not implemented!", this->context))); }; + + RuntimeResult toOther(spObject other); + RuntimeResult virtual toNumber() { return RuntimeResult().failure(makeSharedError(RuntimeError(this->positionStart, this->positionEnd, "toNumber for " + this->type + " is not implemented!", this->context))); } + RuntimeResult virtual toBoolean() { return RuntimeResult().failure(makeSharedError(RuntimeError(this->positionStart, this->positionEnd, "toBoolean for " + this->type + " is not implemented!", this->context))); } + RuntimeResult toNull(); }; struct Number : Object { @@ -76,6 +81,40 @@ struct Number : Object { RuntimeResult unary_plus() override; RuntimeResult unary_minus() override; + + RuntimeResult toNumber() override; + RuntimeResult toBoolean() override; +}; + +struct Boolean : Number { + Boolean() { this->type = "Boolean"; this->isPureZero = 0; this->isPureDouble = true; } + Boolean(bool value); + + std::string to_string() override; + spObject copy() override; + + RuntimeResult toNumber() override; + RuntimeResult toBoolean() override; +}; + +struct Null : Object { + Null() { this->type = "Null"; } + + std::string to_string() override; + spObject copy() override; + + RuntimeResult binary_plus(spObject other) override; + RuntimeResult binary_minus(spObject other) override; + RuntimeResult binary_asterisk(spObject other) override; + RuntimeResult binary_f_slash(spObject other) override; + RuntimeResult binary_double_asterisk(spObject other) override; + RuntimeResult binary_double_f_slash(spObject other) override; + + RuntimeResult unary_plus() override; + RuntimeResult unary_minus() override; + + RuntimeResult toNumber() override; + RuntimeResult toBoolean() override; }; #endif // !OBJECT_H diff --git a/symboltable/SymbolTable.cpp b/symboltable/SymbolTable.cpp index 19dbbba..5290977 100644 --- a/symboltable/SymbolTable.cpp +++ b/symboltable/SymbolTable.cpp @@ -3,8 +3,22 @@ #include #include "../object/object.h" +std::unordered_map globalConstantVariablesTable = { + { "null", makeSharedObject(Null()) }, + { "Infinity", makeSharedObject(Number("Infinity")) }, + { "NaN", makeSharedObject(Number("NaN")) }, + { "true", makeSharedObject(Boolean(true)) }, + { "false", makeSharedObject(Boolean(false)) }, +}; + +bool isGlobalConstantVariable(std::string identifier) { + return globalConstantVariablesTable.find(identifier) != globalConstantVariablesTable.end(); +} spObject SymbolTable::get(std::string key) { + if (isGlobalConstantVariable(key)) { + return globalConstantVariablesTable.at(key); + } if (symbols.find(key) == symbols.end()) { // Key not found if (parent != nullptr) { @@ -18,20 +32,23 @@ spObject SymbolTable::get(std::string key) { } } -bool SymbolTable::set(std::string key, spObject value, bool currentContext) { +SymbolTableSetReturnCode SymbolTable::set(std::string key, spObject value, bool currentContext) { // Returning `false` signifies that there was an error + if (isGlobalConstantVariable(key)) { + return SymbolTableSetReturnCode::errorGlobalConstantVariable; + } if (currentContext) { symbols[key] = value; - return true; + return SymbolTableSetReturnCode::perfect; } else { if (symbols.find(key) != symbols.end()) { symbols[key] = value; - return true; + return SymbolTableSetReturnCode::perfect; } else if (parent != nullptr) { return parent->set(key, value, false); } else { // The symbol doesn't exist anywhere and we weren't told to make it - return false; + return SymbolTableSetReturnCode::errorNotInScope; } } } diff --git a/symboltable/symboltable.h b/symboltable/symboltable.h index 46e19f5..3179027 100644 --- a/symboltable/symboltable.h +++ b/symboltable/symboltable.h @@ -5,6 +5,13 @@ #include #include +enum class SymbolTableSetReturnCode { + perfect, + errorGlobalConstantVariable, + errorUserDefinedConstantVariable, + errorNotInScope, +}; + struct Object; typedef std::shared_ptr spObject; @@ -13,12 +20,16 @@ struct SymbolTable; typedef std::shared_ptr spSymbolTable; +extern std::unordered_map globalConstantVariablesTable; + +extern bool isGlobalConstantVariable(std::string identifier); + struct SymbolTable { std::unordered_map symbols; spSymbolTable parent = nullptr; spObject get(std::string key); - bool set(std::string key, spObject value, bool currentContext = false); + SymbolTableSetReturnCode set(std::string key, spObject value, bool currentContext = false); };