Skip to content

Commit

Permalink
Made a difference between variable declaration and assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
Samathingamajig committed Feb 22, 2021
1 parent 9c91b21 commit 3c2b8ac
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 30 deletions.
2 changes: 1 addition & 1 deletion BarkScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include "interpreter/interpreter.h"
#include "context/context.h"

const std::string bsversion = "0.1.3";
const std::string bsversion = "0.1.4";

int main() {
std::cout << "BarkScript version " << bsversion << std::endl;
Expand Down
21 changes: 20 additions & 1 deletion ast/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace nodetypes {
using namespace std;

const string Number = "NUMBER";
const string VariableDeclaration = "VARDEC";
const string VariableAssignment = "VARASS";
const string VariableRetrievement = "VARRET";
const string BinaryOperator = "BINOP";
Expand Down Expand Up @@ -70,6 +71,24 @@ struct NumberNode : Node {
}
};

struct VariableDeclarationNode : Node {
VariableDeclarationNode(Token token, spNode valueNode) {
this->nodeType = nodetypes::VariableDeclaration;
this->token = token;
this->valueNode = valueNode;
this->positionStart = token.positionStart;
this->positionEnd = valueNode->positionEnd;
}

std::string to_string() override {
return "(LET, identifier:\"" + token.value + "\", EQUAL, " + valueNode->to_string() + ")";
}

operator spNode() override {
return makeSharedNode(*this);
}
};

struct VariableAssignmentNode : Node {
VariableAssignmentNode(Token token, spNode valueNode) {
this->nodeType = nodetypes::VariableAssignment;
Expand All @@ -80,7 +99,7 @@ struct VariableAssignmentNode : Node {
}

std::string to_string() override {
return token.value + " = " + valueNode->to_string();
return "(identifier:\"" + token.value + "\", EQUAL, " + valueNode->to_string() + ")";
}

operator spNode() override {
Expand Down
2 changes: 1 addition & 1 deletion error/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ struct Error {

std::string virtual to_string() {
std::string output = type + ": " + details + '\n';
output += "File " + positionStart.filename + ", line " + std::to_string(positionStart.lineNumber + 1);
output += "File \"" + positionStart.filename + "\", line " + std::to_string(positionStart.lineNumber + 1);
output += "\n\n";
output += strings_with_arrows(positionStart.filetext, positionStart, positionEnd);
return output;
Expand Down
26 changes: 23 additions & 3 deletions interpreter/Interpreter.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include "interpreter.h"
#include <iostream>
#include <string>
#include <stdexcept>
#include "../ast/ast.h"
#include "../object/object.h"

Expand Down Expand Up @@ -33,6 +32,8 @@ RuntimeResult Interpreter::visit(spNode node, spContext context) {

if (type == nodetypes::Number) {
return visitNumberNode(node, context);
} else if (type == nodetypes::VariableDeclaration) {
return visitVariableDeclarationNode(node, context);
} else if (type == nodetypes::VariableAssignment) {
return visitVariableAssignmentNode(node, context);
} else if (type == nodetypes::VariableRetrievement) {
Expand All @@ -53,17 +54,36 @@ RuntimeResult Interpreter::visitNumberNode(spNode node, spContext context) {
return RuntimeResult().success(number);
}

RuntimeResult Interpreter::visitVariableDeclarationNode(spNode node, spContext context) {
RuntimeResult rt;
std::string variableName = node->token.value;
if (context->symbolTable->exists(variableName, false)) {
return rt.failure(RuntimeError(node->positionStart, node->positionEnd, "Variable " + variableName + " is already declared in the current scope!", context));
}
spObject value;
value = rt.registerRT(visit(node->valueNode, context));
if (rt.hasError()) return rt;
SymbolTableSetReturnCode success = context->symbolTable->set(variableName, value, true);
switch (success) {
case SymbolTableSetReturnCode::perfect: { return rt.success(value); }
case SymbolTableSetReturnCode::errorGlobalConstantVariable: { return rt.failure(RuntimeError(node->token.positionStart, node->positionEnd, "You cannot modify a global constant variable!", context)); }
case SymbolTableSetReturnCode::errorUserDefinedConstantVariable: { return rt.failure(RuntimeError(node->token.positionStart, node->positionEnd, "You cannot modify a constant variable!", context)); }
case SymbolTableSetReturnCode::errorNotInScope: { return rt.failure(RuntimeError(node->token.positionStart, node->positionEnd, "Variable \"" + variableName + "\" does not exist in the current scope!", context)); }
default: { return rt.failure(RuntimeError(node->token.positionStart, node->positionEnd, "Unknown return value when setting: " + std::to_string((int) success), context)); }
}
}

RuntimeResult Interpreter::visitVariableAssignmentNode(spNode node, spContext context) {
RuntimeResult rt;
std::string variableName = node->token.value;
spObject value = rt.registerRT(visit(node->valueNode, context));
if (rt.hasError()) return rt;
SymbolTableSetReturnCode success = context->symbolTable->set(variableName, value, true);
SymbolTableSetReturnCode success = context->symbolTable->set(variableName, value, false);
switch (success) {
case SymbolTableSetReturnCode::perfect: { return rt.success(value); }
case SymbolTableSetReturnCode::errorGlobalConstantVariable: { return rt.failure(RuntimeError(node->token.positionStart, node->positionEnd, "You cannot modify a global constant variable!", context)); }
case SymbolTableSetReturnCode::errorUserDefinedConstantVariable: { return rt.failure(RuntimeError(node->token.positionStart, node->positionEnd, "You cannot modify a constant variable!", context)); }
case SymbolTableSetReturnCode::errorNotInScope: { return rt.failure(RuntimeError(node->token.positionStart, node->positionEnd, "Variable " + variableName + " does not exist in the current scope!", context)); }
case SymbolTableSetReturnCode::errorNotInScope: { return rt.failure(RuntimeError(node->token.positionStart, node->positionEnd, "Variable \"" + variableName + "\" does not exist in the current scope!", context)); }
default: { return rt.failure(RuntimeError(node->token.positionStart, node->positionEnd, "Unknown return value when setting: " + std::to_string((int) success), context)); }
}
}
Expand Down
1 change: 1 addition & 0 deletions interpreter/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct Interpreter {
RuntimeResult visit(spNode node, spContext context);

RuntimeResult visitNumberNode(spNode node, spContext context);
RuntimeResult visitVariableDeclarationNode(spNode node, spContext context);
RuntimeResult visitVariableAssignmentNode(spNode node, spContext context);
RuntimeResult visitVariableRetrievementNode(spNode node, spContext context);
RuntimeResult visitBinaryOperatorNode(spNode node, spContext context);
Expand Down
94 changes: 72 additions & 22 deletions parser/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ Token Parser::nextToken() {
return currentToken;
}

Token Parser::peekToken(unsigned int num) {
if (tokenIndex + num + 1 >= tokens.size()) {
return Token();
} else {
return tokens[tokenIndex + num + 1];
}
}

ParseResult Parser::atom() {
ParseResult pr;
Token token = currentToken;
Expand All @@ -40,12 +48,12 @@ ParseResult Parser::atom() {
} else if (token.type == tokens::OPEN_PAREN) {
nextToken();
pr.registerAdvancement();
spNode exprRes = pr.registerPR(expr());
spNode value = pr.registerPR(expr());
if (pr.hasError()) return pr;
if (currentToken.type == tokens::CLOSE_PAREN) {
nextToken();
pr.registerAdvancement();
return pr.success(exprRes);
return pr.success(value);
}
return pr.failure(InvalidSyntaxError(currentToken.positionStart, currentToken.positionEnd, "Expected a ')'"));
} else {
Expand Down Expand Up @@ -77,39 +85,77 @@ ParseResult Parser::term() {
return binaryOperation(rule, { tokens::ASTERISK, tokens::F_SLASH, tokens::DOUBLE_F_SLASH });
}

ParseResult Parser::expr() {
ParseResult Parser::assignment() {
ParseResult pr;
if (currentToken.matches(tokens::KEYWORD, reservedWords::LET)) {
nextToken();
pr.registerAdvancement();
if (!currentToken.matches(tokens::IDENTIFIER))
return pr.failure(InvalidSyntaxError(currentToken.positionStart, currentToken.positionEnd, "Expected an identifier"));
Token variableNameToken = currentToken;
nextToken();
pr.registerAdvancement();
if (!currentToken.matches(tokens::EQUAL))
return pr.failure(InvalidSyntaxError(currentToken.positionStart, currentToken.positionEnd, "Expected an '='"));
if (!currentToken.matches(tokens::IDENTIFIER))
return pr.failure(InvalidSyntaxError(currentToken.positionStart, currentToken.positionEnd, "Expected an identifier"));
Token variableNameToken = currentToken;
nextToken();
pr.registerAdvancement();
if (!currentToken.matches(tokens::EQUAL))
return pr.failure(InvalidSyntaxError(currentToken.positionStart, currentToken.positionEnd, "Expected an '='"));
nextToken();
pr.registerAdvancement();
spNode value = pr.registerPR(expr());
if (pr.hasError()) return pr;
return pr.success(VariableAssignmentNode(variableNameToken, value));
}

ParseResult Parser::declaration() {
ParseResult pr;
nextToken();
pr.registerAdvancement();
Token variableNameToken = currentToken;
if (variableNameToken.type != tokens::IDENTIFIER)
return pr.failure(InvalidSyntaxError(variableNameToken.positionStart, variableNameToken.positionEnd, "Expected an identifier"));
spNode value = nullptr;
nextToken();
pr.registerAdvancement();
if (currentToken.matches(tokens::EQUAL)) {
nextToken();
pr.registerAdvancement();
spNode exprRes = pr.registerPR(expr());
if (pr.hasError()) return pr;
return pr.success(VariableAssignmentNode(variableNameToken, exprRes));
value = pr.registerPR(expr());
} else {
Position posEnd = variableNameToken.positionEnd.copy();
posEnd.columnNumber--; // The Token constructor automatically calls `advance()`
value = VariableRetrievementNode(Token(tokens::IDENTIFIER, "null", variableNameToken.positionStart, posEnd));
}
if (pr.hasError())
return pr;
return pr.success(VariableDeclarationNode(variableNameToken, value));
}

ParseResult Parser::expr() {
ParseResult pr;
if (nextIsAssignment()) {
return assignment();
} else {
std::function<ParseResult()> rule = [this]() { return term(); };
spNode termRes = pr.registerPR(binaryOperation(rule, { tokens::PLUS, tokens::MINUS }));
if (pr.hasError()) {
return pr.failure(InvalidSyntaxError(currentToken.positionStart, currentToken.positionEnd, "Expected a 'let', number, identifier, '+', '-', or a '('"));
return pr.failure(InvalidSyntaxError(currentToken.positionStart, currentToken.positionEnd, "Expected a number, identifier, '+', '-', or a '('"));
}
return pr.success(termRes);
}
}

ParseResult Parser::parse() {
ParseResult pr = expr();
if (!pr.hasError() && currentToken.type != tokens::EEOF) {
return pr.failure(InvalidSyntaxError(currentToken.positionStart, currentToken.positionEnd, "Expected a '+', '-', '*', '/', '**', '//', or a '('"));
ParseResult Parser::statement() {
if (currentToken.matches(tokens::KEYWORD, reservedWords::LET)) {
return declaration();
} else {
ParseResult pr = expr();
if (pr.hasError()) {
return pr.failure(InvalidSyntaxError(currentToken.positionStart, currentToken.positionEnd, "Expected a 'let', number, identifier, '+', '-', or a '('"));
}
if (!pr.hasError() && currentToken.type != tokens::EEOF) {
return pr.failure(InvalidSyntaxError(currentToken.positionStart, currentToken.positionEnd, "Expected a '+', '-', '*', '/', '**', '//', or a '('"));
}
return pr;
}
return pr;
}

ParseResult Parser::parse() {
return statement();
}

ParseResult Parser::binaryOperation(std::function<ParseResult()> rule, std::vector<std::string> allowedTokens) {
Expand All @@ -132,3 +178,7 @@ ParseResult Parser::binaryOperation(std::function<ParseResult()> rule1, std::vec

return pr.success(left);
}

bool Parser::nextIsAssignment() {
return peekToken(0).matches(tokens::EQUAL);
}
6 changes: 6 additions & 0 deletions parser/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,22 @@ struct Parser {
int tokenIndex;

Token nextToken();
Token peekToken(unsigned int index = 0U);

ParseResult atom();
ParseResult exponent();
ParseResult factor();
ParseResult term();
ParseResult assignment();
ParseResult declaration();
ParseResult expr();
ParseResult statement();
ParseResult parse();

ParseResult binaryOperation(std::function<ParseResult()> rule, std::vector<std::string> allowedTokens);
ParseResult binaryOperation(std::function<ParseResult()> rule1, std::vector<std::string> allowedTokens, std::function<ParseResult()> rule2);

bool nextIsAssignment();
};

#endif // !PARSER_H
10 changes: 10 additions & 0 deletions symboltable/SymbolTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,13 @@ SymbolTableSetReturnCode SymbolTable::set(std::string key, spObject value, bool
}
}
}

bool SymbolTable::exists(std::string key, bool deepSearch) {
if (symbols.find(key) != symbols.end()) {
return true;
} else if (deepSearch && parent != nullptr) {
return parent->exists(key, deepSearch);
} else {
return false;
}
}
2 changes: 2 additions & 0 deletions symboltable/symboltable.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ struct SymbolTable {

spObject get(std::string key);
SymbolTableSetReturnCode set(std::string key, spObject value, bool currentContext = false);

bool exists(std::string key, bool deepSearch = true);
};


Expand Down
12 changes: 10 additions & 2 deletions syntax.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
expr : KEYWORD:LET IDENTIFIER EQUAL expr
statement : declaration
: expr

declaration : KEYWORD:LET IDENTIFIER EQUAL expr
: KEYWORD:LET IDENTIFIER

expr : assignment
: term ((PLUS|MINUS) term)*

assignment : IDENTIFIER EQUAL expr

term : factor ((ASTERISK|F_SLASH|DOUBLE_F_SLASH) factor)*

factor : (PLUS|MINUS) factor
: exponent

exponent : atom (DOUBLE_ASTERISK factor)*
exponent : atom (DOUBLE_ASTERISK factor)

atom : NUMBER
: IDENTIFIER
Expand Down

0 comments on commit 3c2b8ac

Please sign in to comment.