9 files modified
1 files added
| | |
| | | #include "Interpreter/LiteralExpressionNode.hpp" |
| | | #include "Interpreter/UnaryExpressionNode.hpp" // <-- új include |
| | | #include "Interpreter/CallExpressionNode.hpp" |
| | | #include "Interpreter/ObjectExpressionNode.hpp" |
| | | #include "Parser/ParsedExpression.hpp" |
| | | |
| | | namespace Parser { |
| | |
| | | } |
| | | return std::make_unique<Interpreter::CallExpressionNode>(expr->name, std::move(callArgs)); |
| | | } |
| | | case Kind::Object: |
| | | { |
| | | std::vector<std::pair<std::string, std::unique_ptr<Interpreter::ExpressionNode>>> members; |
| | | members.reserve(expr->objectMembers.size()); |
| | | for (const auto &p : expr->objectMembers) { |
| | | members.emplace_back(p.first, buildExpressionFromParsed(p.second)); |
| | | } |
| | | return std::make_unique<Interpreter::ObjectExpressionNode>(std::move(members)); |
| | | } |
| | | } |
| | | |
| | | throw std::runtime_error("Unknown ParsedExpression kind"); |
| New file |
| | |
| | | #ifndef INTERPRETER_OBJECT_EXPRESSION_NODE_HPP |
| | | #define INTERPRETER_OBJECT_EXPRESSION_NODE_HPP |
| | | |
| | | #include "ExpressionNode.hpp" |
| | | #include "Symbols/Value.hpp" |
| | | #include <vector> |
| | | #include <string> |
| | | #include <memory> |
| | | |
| | | namespace Interpreter { |
| | | |
| | | class ObjectExpressionNode : public ExpressionNode { |
| | | public: |
| | | using ObjectMap = Symbols::Value::ObjectMap; |
| | | |
| | | explicit ObjectExpressionNode(std::vector<std::pair<std::string, std::unique_ptr<ExpressionNode>>> members) |
| | | : members_(std::move(members)) {} |
| | | |
| | | Symbols::Value evaluate(Interpreter & interpreter) const override { |
| | | ObjectMap obj; |
| | | for (const auto &kv : members_) { |
| | | obj[kv.first] = kv.second->evaluate(interpreter); |
| | | } |
| | | return Symbols::Value(obj); |
| | | } |
| | | |
| | | std::string toString() const override { return "[object]"; } |
| | | |
| | | private: |
| | | std::vector<std::pair<std::string, std::unique_ptr<ExpressionNode>>> members_; |
| | | }; |
| | | |
| | | } // namespace Interpreter |
| | | |
| | | #endif // INTERPRETER_OBJECT_EXPRESSION_NODE_HPP |
| | |
| | | const std::vector<std::string> OPERATOR_LOGICAL = { "&&", "||" }; |
| | | |
| | | const std::vector<std::string> OPERATOR_ARITHMETIC = { "+", "-", "*", "/", "%", "!" }; |
| | | const std::vector<std::string> PUNCTUATION = { "(", ")", "{", "}", "[", "]", ",", ";" }; |
| | | const std::vector<std::string> PUNCTUATION = { "(", ")", "{", "}", "[", "]", ",", ";", ":" }; |
| | | |
| | | bool contains(const std::vector<std::string> & vec, const std::string & value) { |
| | | return std::find(vec.begin(), vec.end(), value) != vec.end(); |
| | |
| | | KEYWORD_DOUBLE, |
| | | KEYWORD_FLOAT, |
| | | KEYWORD_BOOLEAN, |
| | | KEYWORD_OBJECT, |
| | | KEYWORD_FUNCTION_DECLARATION, |
| | | KEYWORD_RETURN, |
| | | KEYWORD_NULL, |
| | |
| | | return "KEYWORD_FLOAT"; |
| | | case Lexer::Tokens::Type::KEYWORD_BOOLEAN: |
| | | return "KEYWORD_BOOLEAN"; |
| | | case Lexer::Tokens::Type::KEYWORD_OBJECT: |
| | | return "KEYWORD_OBJECT"; |
| | | case Lexer::Tokens::Type::KEYWORD_FUNCTION_DECLARATION: |
| | | return "KEYWORD_FUNCTION"; |
| | | case Lexer::Tokens::Type::KEYWORD_RETURN: |
| | |
| | | using ParsedExpressionPtr = std::unique_ptr<ParsedExpression>; |
| | | |
| | | struct ParsedExpression { |
| | | enum class Kind : std::uint8_t { Literal, Variable, Binary, Unary, Call }; |
| | | enum class Kind : std::uint8_t { Literal, Variable, Binary, Unary, Call, Object }; |
| | | |
| | | Kind kind; |
| | | |
| | |
| | | ParsedExpressionPtr rhs; |
| | | // For function call arguments |
| | | std::vector<ParsedExpressionPtr> args; |
| | | std::vector<std::pair<std::string, ParsedExpressionPtr>> objectMembers; |
| | | |
| | | // Constructor for literal |
| | | static ParsedExpressionPtr makeLiteral(const Symbols::Value & val) { |
| | |
| | | expr->kind = Kind::Call; |
| | | expr->name = name; |
| | | expr->args = std::move(arguments); |
| | | return expr; |
| | | } |
| | | // Constructor for object literal |
| | | static ParsedExpressionPtr makeObject(std::vector<std::pair<std::string, ParsedExpressionPtr>> members) { |
| | | auto expr = std::make_unique<ParsedExpression>(); |
| | | expr->kind = Kind::Object; |
| | | expr->objectMembers = std::move(members); |
| | | return expr; |
| | | } |
| | | |
| | |
| | | auto funcSym = std::static_pointer_cast<Symbols::FunctionSymbol>(symbol); |
| | | return funcSym->returnType(); |
| | | } |
| | | case Kind::Object: |
| | | return Symbols::Variables::Type::OBJECT; |
| | | |
| | | default: |
| | | throw std::runtime_error("Unknown expression kind"); |
| | |
| | | #include "Lexer/Operators.hpp" |
| | | // Statements and expression building for conditional and block parsing |
| | | #include "Interpreter/ConditionalStatementNode.hpp" |
| | | // #include "Interpreter/ForStatementNode.hpp" // removed until for-in loops are implemented |
| | | #include "Interpreter/CallStatementNode.hpp" |
| | | #include "Interpreter/DeclareVariableStatementNode.hpp" |
| | | #include "Interpreter/ReturnStatementNode.hpp" |
| | |
| | | { "string", Lexer::Tokens::Type::KEYWORD_STRING }, |
| | | { "boolean", Lexer::Tokens::Type::KEYWORD_BOOLEAN }, |
| | | { "bool", Lexer::Tokens::Type::KEYWORD_BOOLEAN }, |
| | | { "object", Lexer::Tokens::Type::KEYWORD_OBJECT }, |
| | | // ... other keywords ... |
| | | }; |
| | | |
| | |
| | | { Lexer::Tokens::Type::KEYWORD_STRING, Symbols::Variables::Type::STRING }, |
| | | { Lexer::Tokens::Type::KEYWORD_NULL, Symbols::Variables::Type::NULL_TYPE }, |
| | | { Lexer::Tokens::Type::KEYWORD_BOOLEAN, Symbols::Variables::Type::BOOLEAN }, |
| | | { Lexer::Tokens::Type::KEYWORD_OBJECT, Symbols::Variables::Type::OBJECT }, |
| | | }; |
| | | |
| | | void Parser::parseVariableDefinition() { |
| | |
| | | |
| | | while (true) { |
| | | auto token = currentToken(); |
| | | // Object literal: { key: value, ... } |
| | | if (token.type == Lexer::Tokens::Type::PUNCTUATION && token.value == "{") { |
| | | // Consume '{' |
| | | consumeToken(); |
| | | std::vector<std::pair<std::string, ParsedExpressionPtr>> members; |
| | | // Parse members until '}' |
| | | if (!(currentToken().type == Lexer::Tokens::Type::PUNCTUATION && currentToken().value == "}")) { |
| | | while (true) { |
| | | // Optional type tag before key |
| | | Symbols::Variables::Type memberType = Symbols::Variables::Type::UNDEFINED_TYPE; |
| | | if (Parser::variable_types.find(currentToken().type) != Parser::variable_types.end()) { |
| | | memberType = parseType(); |
| | | } |
| | | // Key must be an identifier or variable identifier |
| | | if (currentToken().type != Lexer::Tokens::Type::IDENTIFIER && |
| | | currentToken().type != Lexer::Tokens::Type::VARIABLE_IDENTIFIER) { |
| | | reportError("Expected identifier for object key"); |
| | | } |
| | | std::string key = currentToken().value; |
| | | // Strip '$' if present |
| | | if (!key.empty() && key[0] == '$') { |
| | | key = key.substr(1); |
| | | } |
| | | consumeToken(); |
| | | // Expect ':' delimiter |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ":"); |
| | | // Parse value expression (pass tag type if provided) |
| | | Symbols::Variables::Type expectType = (memberType == Symbols::Variables::Type::UNDEFINED_TYPE) |
| | | ? Symbols::Variables::Type::NULL_TYPE |
| | | : memberType; |
| | | auto valueExpr = parseParsedExpression(expectType); |
| | | members.emplace_back(key, std::move(valueExpr)); |
| | | if (match(Lexer::Tokens::Type::PUNCTUATION, ",")) { |
| | | continue; |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | // Expect closing '}' |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, "}"); |
| | | // Create object literal parsed expression |
| | | output_queue.push_back(ParsedExpression::makeObject(std::move(members))); |
| | | expect_unary = false; |
| | | continue; |
| | | } |
| | | |
| | | if (token.type == Lexer::Tokens::Type::PUNCTUATION && token.lexeme == "(") { |
| | | operator_stack.push("("); |
| | |
| | | void parseReturnStatement(); |
| | | // Parse an if-else conditional statement |
| | | void parseIfStatement(); |
| | | // Parse a for-in loop over object members |
| | | void parseForStatement(); |
| | | // Parse a statement node for use inside blocks (not added to operation container) |
| | | std::unique_ptr<Interpreter::StatementNode> parseStatementNode(); |
| | | |
| | |
| | | #include <algorithm> |
| | | #include <stdexcept> |
| | | #include <string> |
| | | #include <map> |
| | | #include <variant> |
| | | |
| | | #include "VariableTypes.hpp" |
| | |
| | | |
| | | class Value { |
| | | public: |
| | | using Variant = std::variant<int, double, float, std::string, bool>; |
| | | using ObjectMap = std::map<std::string, Value>; |
| | | using Variant = std::variant<int, double, float, std::string, bool, ObjectMap>; |
| | | |
| | | Value() = default; |
| | | |
| | |
| | | Value(const char * v) : value_(std::string(v)) { type_ = Symbols::Variables::Type::STRING; } |
| | | |
| | | Value(bool v) : value_(v) { type_ = Symbols::Variables::Type::BOOLEAN; } |
| | | /** |
| | | * @brief Construct an object value from a map of member names to Values. |
| | | */ |
| | | Value(const ObjectMap & v) : value_(v) { type_ = Symbols::Variables::Type::OBJECT; } |
| | | |
| | | Value(const std::string & str, bool autoDetectType) { *this = fromString(str, autoDetectType); } |
| | | |
| | |
| | | return val ? "true" : "false"; |
| | | } else if constexpr (std::is_same_v<T, std::string>) { |
| | | return val; |
| | | } else if constexpr (std::is_same_v<T, ObjectMap>) { |
| | | return "[object]"; |
| | | } else { |
| | | return std::to_string(val); |
| | | } |
| | |
| | | |
| | | namespace Symbols::Variables { |
| | | |
| | | enum class Type : std::uint8_t { INTEGER, DOUBLE, FLOAT, STRING, BOOLEAN, NULL_TYPE, UNDEFINED_TYPE }; |
| | | enum class Type : std::uint8_t { INTEGER, DOUBLE, FLOAT, STRING, BOOLEAN, OBJECT, NULL_TYPE, UNDEFINED_TYPE }; |
| | | |
| | | const std::unordered_map<std::string, Type> StringToTypeMap = { |
| | | { "int", Type::INTEGER }, |
| | |
| | | { "bool", Type::BOOLEAN }, |
| | | { "boolean", Type::BOOLEAN }, |
| | | { "null", Type::NULL_TYPE }, |
| | | { "object", Type::OBJECT }, |
| | | |
| | | { "undefined", Type::UNDEFINED_TYPE }, |
| | | }; |
| | |
| | | { Type::FLOAT, "float" }, |
| | | { Type::STRING, "string" }, |
| | | { Type::BOOLEAN, "bool" }, |
| | | { Type::OBJECT, "object" }, |
| | | { Type::NULL_TYPE, "null" }, |
| | | { Type::UNDEFINED_TYPE, "undeffined" }, |
| | | { Type::UNDEFINED_TYPE, "undefined" }, |
| | | }; |
| | | |
| | | inline static std::string TypeToString(Symbols::Variables::Type type) { |
| | |
| | | |
| | | object $person = { |
| | | name: "Szoni", |
| | | age: 37 |
| | | double age: 37.6 |
| | | }; |
| | | |
| | | |
| | | object $person2 = { |
| | | string name: "Not Szoni", |
| | | int age: 37, |
| | | object $children: { |
| | | string name: "Child1", |
| | | int age: 10 |
| | | } |
| | | }; |
| | | |
| | | object $children2 = { |
| | | string name: "Child2", |
| | | int age: 15 |
| | | }; |
| | | |
| | | object $family = { |
| | | $children: $children, // this is valid too |
| | | object $children: $children2 |
| | | }; |
| | | |
| | | object $family3 = { |
| | | string age: 10 // this is invalid, drops error |
| | | }; |
| | | |
| | | |
| | | |
| | | printnl("Hello, ", $person->name); // prints out: Hello, Szoni |
| | | println("Type: ", typeof($person); // prints out: Type: object |
| | | println("Type: ", typeof($person2->name)); // prints out: Type: string |
| | | println("Type: ", typeof($person2->age)); // prints out: Type: double |