| src/Interpreter/BinaryExpressionNode.hpp | ●●●●● patch | view | raw | blame | history | |
| src/Interpreter/ConditionalStatementNode.hpp | ●●●●● patch | view | raw | blame | history | |
| src/Interpreter/Interpreter.hpp | ●●●●● patch | view | raw | blame | history | |
| src/Modules/TypeofModule.hpp | ●●●●● patch | view | raw | blame | history | |
| src/Parser/Parser.cpp | ●●●●● patch | view | raw | blame | history | |
| src/Parser/Parser.hpp | ●●●●● patch | view | raw | blame | history | |
| src/VoidScript.hpp | ●●●●● patch | view | raw | blame | history | |
| test_scripts/arguments.vs | ●●●●● patch | view | raw | blame | history | |
| test_scripts/if_statements.vs | ●●●●● patch | view | raw | blame | history | |
| test_scripts/object.vs | ●●●●● patch | view | raw | blame | history | |
| test_scripts/typeof.vs | ●●●●● patch | view | raw | blame | history | |
| tmp.vs | ●●●●● patch | view | raw | blame | history |
src/Interpreter/BinaryExpressionNode.hpp
@@ -94,7 +94,12 @@ if (op_ == "+") { return Symbols::Value(l + r); } if (op_ == "==") { return Symbols::Value(l == r); } if (op_ == "!=") { return Symbols::Value(l != r); } throw std::runtime_error("Unknown operator: " + op_); } src/Interpreter/ConditionalStatementNode.hpp
New file @@ -0,0 +1,57 @@ #ifndef INTERPRETER_CONDITIONAL_STATEMENT_NODE_HPP #define INTERPRETER_CONDITIONAL_STATEMENT_NODE_HPP #include <vector> #include <memory> #include <string> #include "Interpreter/StatementNode.hpp" #include "Interpreter/ExpressionNode.hpp" namespace Interpreter { /** * @brief Statement node representing an if-else conditional block. */ class ConditionalStatementNode : public StatementNode { std::unique_ptr<ExpressionNode> condition_; std::vector<std::unique_ptr<StatementNode>> thenBranch_; std::vector<std::unique_ptr<StatementNode>> elseBranch_; public: ConditionalStatementNode( std::unique_ptr<ExpressionNode> condition, std::vector<std::unique_ptr<StatementNode>> thenBranch, std::vector<std::unique_ptr<StatementNode>> elseBranch, const std::string & file_name, int line, size_t column ) : StatementNode(file_name, line, column), condition_(std::move(condition)), thenBranch_(std::move(thenBranch)), elseBranch_(std::move(elseBranch)) {} void interpret(class Interpreter & interpreter) const override { // Evaluate condition auto val = condition_->evaluate(interpreter); bool cond = false; if (val.getType() == Symbols::Variables::Type::BOOLEAN) { cond = val.get<bool>(); } else { throw std::runtime_error("Condition did not evaluate to boolean at " + filename_ + ":" + std::to_string(line_) + "," + std::to_string(column_)); } // Execute appropriate branch const auto & branch = cond ? thenBranch_ : elseBranch_; for (const auto & stmt : branch) { stmt->interpret(interpreter); } } std::string toString() const override { return "ConditionalStatementNode at " + filename_ + ":" + std::to_string(line_); } }; } // namespace Interpreter #endif // INTERPRETER_CONDITIONAL_STATEMENT_NODE_HPP src/Interpreter/Interpreter.hpp
@@ -65,6 +65,12 @@ op.statement->interpret(*this); } break; case Operations::Type::Conditional: // if-else conditional block if (op.statement) { op.statement->interpret(*this); } break; case Operations::Type::Return: case Operations::Type::Loop: case Operations::Type::Break: @@ -72,7 +78,6 @@ case Operations::Type::Block: case Operations::Type::Import: case Operations::Type::Error: case Operations::Type::Conditional: // TODO: implement these operations later break; default: src/Modules/TypeofModule.hpp
New file @@ -0,0 +1,45 @@ // TypeofModule.hpp #ifndef MODULES_TYPEOFMODULE_HPP #define MODULES_TYPEOFMODULE_HPP #include <string> #include <vector> #include "BaseModule.hpp" #include "ModuleManager.hpp" #include "Symbols/Value.hpp" #include "Symbols/VariableTypes.hpp" namespace Modules { /** * @brief Module providing a typeof() function. * Usage: * typeof($var) -> returns string name of type ("int", "string", etc.) * typeof($var, "int") -> returns bool indicating if type matches */ class TypeofModule : public BaseModule { public: void registerModule() override { auto &mgr = ModuleManager::instance(); mgr.registerFunction("typeof", [](const std::vector<Symbols::Value> &args) { using namespace Symbols; if (args.size() == 1) { auto t = args[0].getType(); return Value(Variables::TypeToString(t)); } else if (args.size() == 2) { auto t = args[0].getType(); std::string name = Variables::TypeToString(t); if (args[1].getType() != Variables::Type::STRING) { throw std::runtime_error("Second argument to typeof must be string"); } bool match = (name == args[1].get<std::string>()); return Value(match); } throw std::runtime_error("typeof expects 1 or 2 arguments"); }); } }; } // namespace Modules #endif // MODULES_TYPEOFMODULE_HPP src/Parser/Parser.cpp
@@ -4,6 +4,13 @@ #include "Interpreter/OperationsFactory.hpp" #include "Lexer/Operators.hpp" // Statements and expression building for conditional and block parsing #include "Interpreter/ConditionalStatementNode.hpp" #include "Interpreter/CallStatementNode.hpp" #include "Interpreter/DeclareVariableStatementNode.hpp" #include "Interpreter/ReturnStatementNode.hpp" #include "Symbols/SymbolContainer.hpp" #include "Interpreter/ExpressionBuilder.hpp" // Additional necessary includes, if needed namespace Parser { @@ -58,6 +65,96 @@ expect(Lexer::Tokens::Type::PUNCTUATION, ";"); } // Parse an if-else conditional statement void Parser::parseIfStatement() { // 'if' auto ifToken = expect(Lexer::Tokens::Type::KEYWORD, "if"); expect(Lexer::Tokens::Type::PUNCTUATION, "("); auto condExpr = parseParsedExpression(Symbols::Variables::Type::BOOLEAN); expect(Lexer::Tokens::Type::PUNCTUATION, ")"); expect(Lexer::Tokens::Type::PUNCTUATION, "{"); // then branch std::vector<std::unique_ptr<Interpreter::StatementNode>> thenBranch; while (!(currentToken().type == Lexer::Tokens::Type::PUNCTUATION && currentToken().value == "}")) { thenBranch.push_back(parseStatementNode()); } expect(Lexer::Tokens::Type::PUNCTUATION, "}"); // else branch std::vector<std::unique_ptr<Interpreter::StatementNode>> elseBranch; if (match(Lexer::Tokens::Type::KEYWORD, "else")) { expect(Lexer::Tokens::Type::PUNCTUATION, "{"); while (!(currentToken().type == Lexer::Tokens::Type::PUNCTUATION && currentToken().value == "}")) { elseBranch.push_back(parseStatementNode()); } expect(Lexer::Tokens::Type::PUNCTUATION, "}"); } // build condition node auto condNode = buildExpressionFromParsed(condExpr); auto stmt = std::make_unique<Interpreter::ConditionalStatementNode>( std::move(condNode), std::move(thenBranch), std::move(elseBranch), this->current_filename_, ifToken.line_number, ifToken.column_number); // add conditional operation Operations::Container::instance()->add( Symbols::SymbolContainer::instance()->currentScopeName(), Operations::Operation{Operations::Type::Conditional, "", std::move(stmt)}); } // Parse a single statement and return its StatementNode (for use in blocks) std::unique_ptr<Interpreter::StatementNode> Parser::parseStatementNode() { // Return statement if (currentToken().type == Lexer::Tokens::Type::KEYWORD_RETURN) { auto tok = expect(Lexer::Tokens::Type::KEYWORD_RETURN); ParsedExpressionPtr expr = nullptr; if (!(currentToken().type == Lexer::Tokens::Type::PUNCTUATION && currentToken().value == ";")) { expr = parseParsedExpression(Symbols::Variables::Type::NULL_TYPE); } expect(Lexer::Tokens::Type::PUNCTUATION, ";"); auto exprNode = expr ? buildExpressionFromParsed(expr) : nullptr; return std::make_unique<Interpreter::ReturnStatementNode>( std::move(exprNode), this->current_filename_, tok.line_number, tok.column_number); } // Function call statement if (currentToken().type == Lexer::Tokens::Type::IDENTIFIER && peekToken().type == Lexer::Tokens::Type::PUNCTUATION && peekToken().value == "(") { auto idTok = expect(Lexer::Tokens::Type::IDENTIFIER); std::string funcName = idTok.value; expect(Lexer::Tokens::Type::PUNCTUATION, "("); std::vector<ParsedExpressionPtr> args; if (!(currentToken().type == Lexer::Tokens::Type::PUNCTUATION && currentToken().value == ")")) { while (true) { args.push_back(parseParsedExpression(Symbols::Variables::Type::NULL_TYPE)); if (match(Lexer::Tokens::Type::PUNCTUATION, ",")) continue; break; } } expect(Lexer::Tokens::Type::PUNCTUATION, ")"); expect(Lexer::Tokens::Type::PUNCTUATION, ";"); std::vector<std::unique_ptr<Interpreter::ExpressionNode>> exprs; exprs.reserve(args.size()); for (auto &p : args) { exprs.push_back(buildExpressionFromParsed(p)); } return std::make_unique<Interpreter::CallStatementNode>( funcName, std::move(exprs), this->current_filename_, idTok.line_number, idTok.column_number); } // Variable declaration if (Parser::variable_types.find(currentToken().type) != Parser::variable_types.end()) { auto type = parseType(); auto idTok = expect(Lexer::Tokens::Type::VARIABLE_IDENTIFIER); std::string name = idTok.value; if (!name.empty() && name[0] == '$') name = name.substr(1); expect(Lexer::Tokens::Type::OPERATOR_ASSIGNMENT, "="); auto valExpr = parseParsedExpression(type); expect(Lexer::Tokens::Type::PUNCTUATION, ";"); auto exprNode = buildExpressionFromParsed(valExpr); return std::make_unique<Interpreter::DeclareVariableStatementNode>( name, Symbols::SymbolContainer::instance()->currentScopeName(), type, std::move(exprNode), this->current_filename_, idTok.line_number, idTok.column_number); } reportError("Unexpected token in block"); return nullptr; } void Parser::parseFunctionDefinition() { expect(Lexer::Tokens::Type::KEYWORD_FUNCTION_DECLARATION); Lexer::Tokens::Token id_token = expect(Lexer::Tokens::Type::IDENTIFIER); src/Parser/Parser.hpp
@@ -6,6 +6,7 @@ #include <vector> #include "BaseException.hpp" #include "Interpreter/StatementNode.hpp" #include "Lexer/Token.hpp" #include "Lexer/TokenType.hpp" #include "Parser/ParsedExpression.hpp" @@ -169,6 +170,11 @@ // parseStatement (updated to handle return) void parseStatement() { const auto & token_type = currentToken().type; // if-else conditional if (token_type == Lexer::Tokens::Type::KEYWORD && currentToken().value == "if") { parseIfStatement(); return; } if (token_type == Lexer::Tokens::Type::KEYWORD_FUNCTION_DECLARATION) { parseFunctionDefinition(); @@ -201,6 +207,10 @@ void parseCallStatement(); // Parse a return statement (e.g., return; or return expr;) void parseReturnStatement(); // Parse an if-else conditional statement void parseIfStatement(); // Parse a statement node for use inside blocks (not added to operation container) std::unique_ptr<Interpreter::StatementNode> parseStatementNode(); // --- Parsing helper functions --- src/VoidScript.hpp
@@ -9,6 +9,7 @@ #include "Modules/ModuleManager.hpp" #include "Modules/PrintNlModule.hpp" #include "Modules/PrintModule.hpp" #include "Modules/TypeofModule.hpp" #include "Parser/Parser.hpp" class VoidScript { @@ -54,6 +55,8 @@ // Register built-in modules (print, etc.) Modules::ModuleManager::instance().addModule(std::make_unique<Modules::PrintModule>()); Modules::ModuleManager::instance().addModule(std::make_unique<Modules::PrintNlModule>()); // typeof() builtin Modules::ModuleManager::instance().addModule(std::make_unique<Modules::TypeofModule>()); this->files.emplace(this->files.begin(), file); lexer->setKeyWords(Parser::Parser::keywords); test_scripts/arguments.vs
New file @@ -0,0 +1,4 @@ if ($argc > 1) { printnl("Hello,", $argv[1]); } test_scripts/if_statements.vs
New file @@ -0,0 +1,21 @@ string $test = "Test string"; if ($test == "Test string") { printnl("Test passed"); } else { printnl("Test failed"); } bool $this_is_okay = true; bool $this_is_not_okay = false; if ($this_is_okay && $this_is_not_okay == false && $test == "Test string") { printnl("This is okay"); } if ($this_is_okay == false) { printnl("This is not okay"); } test_scripts/object.vs
New file @@ -0,0 +1,4 @@ object $person = { name: "Szoni", age: 37 }; test_scripts/typeof.vs
New file @@ -0,0 +1,7 @@ string $var1 = "Hello"; printnl(typeof($var1)); printnl("This is string: ", typeof($var1,"string")); printnl("This is string: ", typeof($var1,"bool")); printnl("This is a \"something\": ", typeof($var1,"something")); tmp.vs
New file @@ -0,0 +1,4 @@ int $x = 5; printnl(typeof($x)); printnl(typeof($x, "int")); printnl(typeof($x, "string"));