Skip to content

Commit

Permalink
feat: add support for f-string
Browse files Browse the repository at this point in the history
  • Loading branch information
xXenvy committed Aug 14, 2024
1 parent dc64573 commit 10936b6
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 94 deletions.
191 changes: 116 additions & 75 deletions src/execution/evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,42 +41,34 @@ void Evaluator::evaluate(ASTBase* ast) {
}
}

void Evaluator::evaluateProgram(ASTProgram* program) {
inline void Evaluator::evaluateProgram(ASTProgram* program) {
env.enterNewBlock(); // Main program code block.
for (const std::unique_ptr<ASTBase>& child : program->body) {
if(child.get() != nullptr) evaluate(child.get());
}
env.exitCurrentBlock(); // We need to do that, cuz gc need to free memory on main block.
}

void Evaluator::evaluateFunctionDeclaration(ASTFunction* function) {
inline void Evaluator::evaluateFunctionDeclaration(ASTFunction* function) {
env.declareFunction(function->name, function);
}

void Evaluator::evaluateFunctionCall(ASTFunctionCall* functionCall) {
inline void Evaluator::evaluateFunctionCall(ASTFunctionCall* functionCall) {
ASTFunction* func = env.getFunction(functionCall->name, functionCall->line);

env.enterNewBlock();
for (const std::unique_ptr<ASTBase>& child : func->body) {
evaluate(child.get());
}
env.exitCurrentBlock();
}

void Evaluator::evaluatePrint(ASTPrint* print) {
inline void Evaluator::evaluatePrint(ASTPrint* print) {
const std::string value = evaluateExpression(print->expression.get());
std::cout << value << (print->newLine ? "\n" : "");
}

std::string Evaluator::evaluateReadLine(ASTReadLine* read) {
std::string input;
if (read->out.get() != nullptr) {
std::cout << evaluateExpression(read->out.get());
}
std::getline(std::cin, input);
return input;
}

void Evaluator::evaluateVariableDeclaration(ASTVariableDeclaration* declaration) {
inline void Evaluator::evaluateVariableDeclaration(ASTVariableDeclaration* declaration) {
if (declaration->value.get() != nullptr) {
typeChecker.checkType(declaration->varType, declaration->value.get());
ASTValue* varValue = new ASTValue(
Expand All @@ -89,7 +81,7 @@ void Evaluator::evaluateVariableDeclaration(ASTVariableDeclaration* declaration)
env.declareVariable(declaration->name, declaration);
}

void Evaluator::evaluateVariableModify(ASTVariableModify* variableModify) {
inline void Evaluator::evaluateVariableModify(ASTVariableModify* variableModify) {
ASTVariableDeclaration* declaration = env.getVariable(variableModify->name, variableModify->line);
typeChecker.checkType(declaration->varType, variableModify->value.get());

Expand All @@ -101,6 +93,55 @@ void Evaluator::evaluateVariableModify(ASTVariableModify* variableModify) {
declaration->value.reset(newValue);
}

inline void Evaluator::evaluateCondition(ASTCondition* condition) {
const std::string value = evaluateExpression(condition->expression.get());

env.enterNewBlock();
for (const std::unique_ptr<ASTBase>& child : stringToBool(value) ? condition->body : condition->elseBody) {
evaluate(child.get());
}
env.exitCurrentBlock();
}

std::string Evaluator::evaluateReadLine(ASTReadLine* read) {
std::string input;
if (read->out.get() != nullptr) {
std::cout << evaluateExpression(read->out.get());
}
std::getline(std::cin, input);
return input;
}

std::string Evaluator::evaluateFString(ASTFString* fString) {
std::string result;
size_t start = 0;
std::string value = fString->value;

while (start < value.size()) {
size_t braceOpen = value.find('{', start);
if (braceOpen == std::string::npos) {
// There is no further brackets.
result += value.substr(start);
break;
}

result += value.substr(start, braceOpen - start);
size_t braceClose = value.find('}', braceOpen);
if (braceClose == std::string::npos) {
throw ZynkError(
ZynkErrorType::RuntimeError,
"Unmatched '{' in f-string.",
fString->line
);
}

std::string expression = value.substr(braceOpen + 1, braceClose - braceOpen - 1);
result += evaluateExpression(expression, fString->line);
start = braceClose + 1;
}
return result;
}

std::string Evaluator::evaluateTypeCast(ASTTypeCast* typeCast) {
std::string base = evaluateExpression(typeCast->value.get());

Expand All @@ -111,7 +152,7 @@ std::string Evaluator::evaluateTypeCast(ASTTypeCast* typeCast) {
} catch (const std::invalid_argument&) {
throw ZynkError(
ZynkErrorType::TypeCastError,
"Invalid argument: unable to convert the provided value to an integer.",
"Invalid argument. Unable to convert the provided value to an integer.",
typeCast->line
);
}
Expand All @@ -128,10 +169,7 @@ std::string Evaluator::evaluateTypeCast(ASTTypeCast* typeCast) {
case ASTValueType::String:
return base; // Expressions by default are always strings.
case ASTValueType::Bool: {
if (!stringToBool(base)) {
return "false";
}
return "true";
return stringToBool(base) ? "true" : "false";
}
default: {
// This should never happen, but whatever
Expand All @@ -144,14 +182,54 @@ std::string Evaluator::evaluateTypeCast(ASTTypeCast* typeCast) {
}
}

void Evaluator::evaluateCondition(ASTCondition* condition) {
const std::string value = evaluateExpression(condition->expression.get());

env.enterNewBlock();
for (const std::unique_ptr<ASTBase>& child : stringToBool(value) ? condition->body : condition->elseBody) {
evaluate(child.get());
std::string Evaluator::evaluateBinaryOperation(ASTBinaryOperation* operation) {
ASTValueType valueTypes[2] = {
typeChecker.determineType(operation->left.get()),
typeChecker.determineType(operation->right.get())
};
for (ASTValueType& valueType : valueTypes) {
if (valueType != ASTValueType::Integer && valueType != ASTValueType::Float) {
throw ZynkError(
ZynkErrorType::ExpressionError,
"Cannot perform BinaryOperation on '" + typeChecker.typeToString(valueType) + "' type.",
operation->line
);
}
}
env.exitCurrentBlock();
const std::string left = evaluateExpression(operation->left.get());
const std::string right = evaluateExpression(operation->right.get());
try {
return calculateString(left, right, operation->op);
} catch (const ZynkError& err) {
throw ZynkError(err.base_type, err.what(), operation->line);
}
}

std::string Evaluator::evaluateOrOperation(ASTOrOperation* operation) {
const std::string left = evaluateExpression(operation->left.get());
const std::string right = evaluateExpression(operation->right.get());
if (stringToBool(left)) return left;
return right;
}

std::string Evaluator::evaluateAndOperation(ASTAndOperation* operation) {
const std::string left = evaluateExpression(operation->left.get());
const std::string right = evaluateExpression(operation->right.get());
if (!stringToBool(left)) return left;
return right;
}

std::string Evaluator::evaluateExpression(const std::string& expression, size_t line) {
Lexer lexer(expression);
const std::vector<Token> tokens = lexer.tokenize();

Parser parser(tokens);
const auto astExpression = parser.parseExpression(0);

// We set the line number manually here because the parser doesn’t know where
// this expression came from. Without this, error messages would be wrong.
astExpression.get()->line = line;
return evaluateExpression(astExpression.get());
}

std::string Evaluator::evaluateExpression(ASTBase* expression) {
Expand All @@ -162,50 +240,19 @@ std::string Evaluator::evaluateExpression(ASTBase* expression) {
case ASTType::Variable: {
const auto var = static_cast<ASTVariable*>(expression);
return evaluateExpression(env.getVariable(var->name, var->line)->value.get());
};
case ASTType::ReadLine: {
}
case ASTType::ReadLine:
return evaluateReadLine(static_cast<ASTReadLine*>(expression));
};
case ASTType::TypeCast: {
case ASTType::TypeCast:
return evaluateTypeCast(static_cast<ASTTypeCast*>(expression));
};
case ASTType::BinaryOperation: {
const auto operation = static_cast<ASTBinaryOperation*>(expression);
ASTValueType valueTypes[2] = {
typeChecker.determineType(operation->left.get()),
typeChecker.determineType(operation->right.get())
};
for (ASTValueType& valueType : valueTypes) {
if (valueType != ASTValueType::Integer && valueType != ASTValueType::Float) {
throw ZynkError(
ZynkErrorType::ExpressionError,
"Cannot perform BinaryOperation on '" + typeChecker.typeToString(valueType) + "' type.",
operation->line
);
}
}
const std::string left = evaluateExpression(operation->left.get());
const std::string right = evaluateExpression(operation->right.get());
try {
return calculateString(left, right, operation->op);
} catch (const ZynkError& err) {
throw ZynkError(err.base_type, err.what(), operation->line);
}
};
case ASTType::OrOperation: {
const auto operation = static_cast<ASTOrOperation*>(expression);
const std::string left = evaluateExpression(operation->left.get());
const std::string right = evaluateExpression(operation->right.get());
if (stringToBool(left)) return left;
return right;
};
case ASTType::AndOperation: {
const auto operation = static_cast<ASTAndOperation*>(expression);
const std::string left = evaluateExpression(operation->left.get());
const std::string right = evaluateExpression(operation->right.get());
if (!stringToBool(left)) return left;
return right;
};
case ASTType::FString:
return evaluateFString(static_cast<ASTFString*>(expression));
case ASTType::BinaryOperation:
return evaluateBinaryOperation(static_cast<ASTBinaryOperation*>(expression));
case ASTType::OrOperation:
return evaluateOrOperation(static_cast<ASTOrOperation*>(expression));
case ASTType::AndOperation:
return evaluateAndOperation(static_cast<ASTAndOperation*>(expression));
default:
throw ZynkError(
ZynkErrorType::RuntimeError,
Expand Down Expand Up @@ -234,12 +281,6 @@ std::string calculateString(const std::string& left_value, const std::string& ri
if (op == "<=") return left_value <= right_value ? "true" : "false";
if (op == "==") return left_value == right_value ? "true" : "false";
if (op == "!=") return left_value != right_value ? "true" : "false";
if (op == "or") {
return (stringToBool(left_value) == true || stringToBool(right_value) == true) ? "true" : "false";
}
if (op == "and") {
return (stringToBool(left_value) == true && stringToBool(right_value) == true) ? "true" : "false";
}

const bool leftIsFloat = left_value.find('.') != std::string::npos;
const bool rightIsFloat = right_value.find('.') != std::string::npos;
Expand Down
21 changes: 14 additions & 7 deletions src/execution/include/evaluator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#define EVALUATOR_H

#include "../../parsing/include/ast.hpp"
#include "../../parsing/include/lexer.hpp"
#include "../../parsing/include/parser.hpp"
#include "../../typechecker/include/checker.hpp"
#include "runtime.hpp"

Expand All @@ -15,16 +17,21 @@ class Evaluator {
TypeChecker typeChecker;

std::string evaluateExpression(ASTBase* expression);
std::string evaluateExpression(const std::string& expression, size_t line);
std::string evaluateReadLine(ASTReadLine* read);
std::string evaluateTypeCast(ASTTypeCast* typeCast);
std::string evaluateFString(ASTFString* fString);
std::string evaluateBinaryOperation(ASTBinaryOperation* operation);
std::string evaluateAndOperation(ASTAndOperation* operation);
std::string evaluateOrOperation(ASTOrOperation* operation);

void evaluateVariableDeclaration(ASTVariableDeclaration* variable);
void evaluateVariableModify(ASTVariableModify* variableModify);
void evaluateProgram(ASTProgram* program);
void evaluateFunctionDeclaration(ASTFunction* function);
void evaluateFunctionCall(ASTFunctionCall* functionCall);
void evaluatePrint(ASTPrint* print);
void evaluateCondition(ASTCondition* condition);
inline void evaluateVariableDeclaration(ASTVariableDeclaration* variable);
inline void evaluateVariableModify(ASTVariableModify* variableModify);
inline void evaluateProgram(ASTProgram* program);
inline void evaluateFunctionDeclaration(ASTFunction* function);
inline void evaluateFunctionCall(ASTFunctionCall* functionCall);
inline void evaluatePrint(ASTPrint* print);
inline void evaluateCondition(ASTCondition* condition);
};

inline bool stringToBool(const std::string& value);
Expand Down
11 changes: 9 additions & 2 deletions src/parsing/include/ast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ enum class ASTType {
ReadLine,
Value,
Variable,
FString,
BinaryOperation,
Condition,
TypeCast,
AndOperation,
OrOperation
OrOperation,
};

enum class ASTValueType {
Expand All @@ -32,7 +33,7 @@ enum class ASTValueType {

struct ASTBase {
const ASTType type;
const size_t line;
size_t line;

ASTBase(ASTType type, size_t line) : type(type), line(line) {}
};
Expand Down Expand Up @@ -77,6 +78,12 @@ struct ASTVariableModify : public ASTBase {
std::unique_ptr<ASTBase> value;
};

struct ASTFString : public ASTBase {
ASTFString(const std::string& value, size_t line)
: ASTBase(ASTType::FString, line), value(value) {}
const std::string value;
};

struct ASTValue : public ASTBase {
ASTValue(const std::string& value, ASTValueType type, size_t line)
: ASTBase(ASTType::Value, line), value(value), valueType(type) {}
Expand Down
2 changes: 1 addition & 1 deletion src/parsing/include/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ class Parser {
std::unique_ptr<ASTBase> parsePrint(bool newLine);
std::unique_ptr<ASTBase> parseRead();
std::unique_ptr<ASTBase> parseTypeCast(TokenType type);
std::unique_ptr<ASTBase> parseExpression(int priority);
std::unique_ptr<ASTBase> parsePrimaryExpression();
std::unique_ptr<ASTBase> parseIfStatement();
public:
Parser(const std::vector<Token>& tokens);
std::unique_ptr<ASTProgram> parse();
std::unique_ptr<ASTBase> parseCurrent();
std::unique_ptr<ASTBase> parseExpression(int priority);
};
#endif // PARSER_H
10 changes: 8 additions & 2 deletions src/parsing/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ std::unique_ptr<ASTBase> Parser::parseCurrent() {
moveForward();
if (currentToken().type == TokenType::LBRACKET) return parseFunctionCall();
if (currentToken().type == TokenType::ASSIGN) return parseVariableModify();
[[fallthrough]];
return std::make_unique<ASTVariable>(current.value, current.line);
}
default:
throw ZynkError(
Expand Down Expand Up @@ -243,8 +243,14 @@ std::unique_ptr<ASTBase> Parser::parsePrimaryExpression() {
return std::make_unique<ASTValue>(current.value, ASTValueType::Bool, currentLine);
case TokenType::NONE:
return std::make_unique<ASTValue>(current.value, ASTValueType::None, currentLine);
case TokenType::IDENTIFIER:
case TokenType::IDENTIFIER: {
if (currentToken().type == TokenType::STRING && current.value == "f") {
const std::string fStringValue = currentToken().value;
moveForward();
return std::make_unique<ASTFString>(fStringValue, currentLine);
}
return std::make_unique<ASTVariable>(current.value, currentLine);
}
case TokenType::READLINE: {
position--;
std::unique_ptr<ASTBase> expr = parseRead();
Expand Down
4 changes: 2 additions & 2 deletions src/typechecker/checker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ ASTValueType TypeChecker::determineType(ASTBase* expression) {
switch (expression->type) {
case ASTType::TypeCast:
return static_cast<ASTTypeCast*>(expression)->castType;
case ASTType::FString:
case ASTType::ReadLine:
return ASTValueType::String;
case ASTType::Value: {
case ASTType::Value:
return static_cast<ASTValue*>(expression)->valueType;
}
case ASTType::Variable: {
ASTVariable* var = static_cast<ASTVariable*>(expression);
ASTVariableDeclaration* declaration = env.getVariable(var->name, var->line);
Expand Down
Loading

0 comments on commit 10936b6

Please sign in to comment.