From 826382bc90750f13c95812658dc5bd3a535d4519 Mon Sep 17 00:00:00 2001 From: Oleksandr Nemesh Date: Fri, 1 Nov 2024 20:46:39 +0200 Subject: [PATCH] add ExecuteNode --- include/rift/nodes/execute.hpp | 45 ++++++++++++++++++++++++++++++++++ include/rift/nodes/node.hpp | 1 + include/rift/parser.hpp | 5 +++- include/rift/token.hpp | 1 + include/rift/visitor.hpp | 7 ++++++ src/lexer.cpp | 1 + src/nodes/execute.cpp | 17 +++++++++++++ src/parser.cpp | 17 ++++++++++++- src/visitor.cpp | 6 +++++ test/main.cpp | 1 + 10 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 include/rift/nodes/execute.hpp create mode 100644 src/nodes/execute.cpp diff --git a/include/rift/nodes/execute.hpp b/include/rift/nodes/execute.hpp new file mode 100644 index 0000000..8b451e8 --- /dev/null +++ b/include/rift/nodes/execute.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "node.hpp" +#include "../value.hpp" + +#include +#include + +namespace rift { + + /// @brief A node in the AST that should be evaluated as a sub-expression. (used for sub-string interpolation) + class ExecuteNode : public Node { + public: + /// @brief Construct the execute node. + /// @param child The child node to execute. + explicit ExecuteNode(Node* child) : m_child(child) {} + + ~ExecuteNode() override { + delete m_child; + } + + /// @brief Get the child node to execute. + /// @return The child node to execute. + [[nodiscard]] const Node* getChild() const { return m_child; } + + /// @copydoc Node::getType + [[nodiscard]] Type getType() const override { return Type::Execute; } + + /// @copydoc Node::accept + void accept(Visitor* visitor) override; + + /// @copydoc Node::getValue + [[nodiscard]] Value getValue(Visitor* visitor) const override; + + /// @copydoc Node::print + std::ostream& print(std::ostream& out) const override { + out << "ExecuteNode(" << *m_child << ')'; + return out; + } + + private: + Node* m_child; + }; + +} \ No newline at end of file diff --git a/include/rift/nodes/node.hpp b/include/rift/nodes/node.hpp index abe21a6..d479ec6 100644 --- a/include/rift/nodes/node.hpp +++ b/include/rift/nodes/node.hpp @@ -18,6 +18,7 @@ namespace rift { BinaryOp, FunctionCall, TernaryOp, + Execute }; /// @brief Destruct the node. diff --git a/include/rift/parser.hpp b/include/rift/parser.hpp index dc4bcee..b1f7388 100644 --- a/include/rift/parser.hpp +++ b/include/rift/parser.hpp @@ -31,6 +31,8 @@ namespace rift { * * power : call ('^' factor)* * + * interpolate : '$' call + * * call : atom ('(' (expression (',' expression)*)? ')')? * * atom : number | string | identifier @@ -60,7 +62,7 @@ namespace rift { // Parse functions Result parseBlock(); - inline Result parseExpression() { return parseTernaryOp(); } + Result parseExpression() { return parseTernaryOp(); } Result parseTernaryOp(); Result parseBooleanMath(); Result parseComparisonExpression(); @@ -68,6 +70,7 @@ namespace rift { Result parseTerm(); Result parseFactor(); Result parsePower(); + Result parseInterpolate(); Result parseCall(); Result parseAtom(); diff --git a/include/rift/token.hpp b/include/rift/token.hpp index 1fb6287..77e60ae 100644 --- a/include/rift/token.hpp +++ b/include/rift/token.hpp @@ -20,6 +20,7 @@ namespace rift { PLUS, MINUS, STAR, SLASH, PERCENT, CARET, QUESTION, COLON, NULL_COALESCE, ASSIGN, LESS, GREATER, LESS_EQUAL, GREATER_EQUAL, EQUAL_EQUAL, NOT_EQUAL, + DOLLAR, // Logical operators AND, OR, NOT, diff --git a/include/rift/visitor.hpp b/include/rift/visitor.hpp index 5db200c..c3c5c4d 100644 --- a/include/rift/visitor.hpp +++ b/include/rift/visitor.hpp @@ -22,6 +22,10 @@ namespace rift { /// @return The value of the variable. [[nodiscard]] Value getVariable(const std::string& name) const; + /// @brief Get all the variables. + /// @return The variables. + [[nodiscard]] const std::unordered_map* getVariables() const { return m_variables; } + /// @brief Visit a node. void visit(Node* node); @@ -49,6 +53,9 @@ namespace rift { /// @brief Visit a ternary operation node. void visit(class TernaryNode* node); + /// @brief Visit an execute node. + void visit(class ExecuteNode* node); + /// @brief Write a string to the output. /// @param text The text to write. void write(std::string_view text); diff --git a/src/lexer.cpp b/src/lexer.cpp index c050dd8..d948de2 100644 --- a/src/lexer.cpp +++ b/src/lexer.cpp @@ -71,6 +71,7 @@ namespace rift { case '/': return createToken(TokenType::SLASH, "/"); case '%': return createToken(TokenType::PERCENT, "%"); case '^': return createToken(TokenType::CARET, "^"); + case '$': return createToken(TokenType::DOLLAR, "$"); case ':': { if (peek() == '=') { advance(); diff --git a/src/nodes/execute.cpp b/src/nodes/execute.cpp new file mode 100644 index 0000000..d4e0127 --- /dev/null +++ b/src/nodes/execute.cpp @@ -0,0 +1,17 @@ +#include +#include + +namespace rift { + + void ExecuteNode::accept(Visitor* visitor) { + visitor->visit(this); + } + + Value ExecuteNode::getValue(Visitor* visitor) const { + auto value = m_child->getValue(visitor); + if (value.isString()) { + return Value::from(rift::format(value.getString(), *visitor->getVariables())); + } + return value; + } +} diff --git a/src/parser.cpp b/src/parser.cpp index 927e1df..52cb387 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -175,7 +176,7 @@ namespace rift { } Result Parser::parsePower() { - auto res = parseCall(); + auto res = parseInterpolate(); if (!res) return res; auto* expression = res.getValue(); while (m_currentToken.type == TokenType::CARET) { @@ -190,6 +191,20 @@ namespace rift { return Result::success(expression); } + Result Parser::parseInterpolate() { + if (m_currentToken.type != TokenType::DOLLAR) { + return parseCall(); + } + advance(); + auto res = parseCall(); + if (!res) { + return res; + } + auto* expression = res.getValue(); + auto* execute = new ExecuteNode(expression); + return Result::success(execute); + } + Result Parser::parseCall() { auto res = parseAtom(); if (m_currentToken.type != TokenType::LEFT_PAREN || !res) { diff --git a/src/visitor.cpp b/src/visitor.cpp index 84c7b3d..cb584cd 100644 --- a/src/visitor.cpp +++ b/src/visitor.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -74,6 +75,11 @@ namespace rift { write(value.toString()); } + void Visitor::visit(ExecuteNode* node) { + auto value = node->getValue(this); + write(value.toString()); + } + const std::string& Visitor::evaluate() { for (auto& node : m_script->m_nodes) { node->accept(this); diff --git a/test/main.cpp b/test/main.cpp index 5af54c8..21ba6df 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -91,6 +91,7 @@ int main() { RIFT_TEST_CASE("{duration(123.456)}", "2:03.456"); RIFT_TEST_CASE("{myCustomFunc('World')}", "Hello, World!"); + RIFT_TEST_CASE("{$'string interpolation: {2 + 2}'}", "string interpolation: 4"); } // Show the results