diff --git a/README.md b/README.md index 1edd73c..328b464 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ The sQeeZ Parser is responsible for analyzing the sequence of tokens generated b - [Expressions](#expressions) - [Overview of the Node Types](#overview-of-the-node-types) - [Order of Precedence](#order-of-precedence) + - [Operator Precedence Levels](#operator-precedence-levels) + - [Parsing Functions Overview](#parsing-functions-overview) # How to Use > **Note:** @@ -184,16 +186,17 @@ enum class NodeType { FunctionDeclaration, ReturnStmt, VarDeclaration, - ConditionalStatement, - WhileStatement, - DoWhileStatement, - ForStatement, - ForInStatement, - ForOfStatement, + ConditionalStmt, + WhileStmt, + DoWhileStmt, + ForStmt, + ForInStmt, + ForOfStmt, LogStmt, // EXPRESSIONS AssignmentExpr, CompoundAssignmentExpr, + CallbackFunctionExpr, TernaryExpr, BinaryExpr, UnaryExpr, @@ -212,42 +215,93 @@ enum class NodeType { StringLiteral, HexCodeLiteral, // Short Notation - ShortOperationLiteral, - ShortSingleExpressionLiteral, - ShortDoubleExpressionLiteral + ShortOperationLiteral }; ``` # Order of Precedence The order of precedence dictates how operators are parsed and evaluated in expressions, which is vital for constructing accurate Abstract Syntax Trees (AST). The parser employs recursive descent parsing, where each level of the tree represents different precedence levels. This recursive nature allows for a clear and structured representation of nested expressions, ensuring that operations are performed in the correct order according to their precedence. -1. **Primary Expressions** - - Literals, Identifiers, parenthesized expressions. -2. **Short Notation (Data)** - - `@` (short notation for objects and arrays) -2. **Short Notation (Functions)** - - `MAP`, `FILTER`, `REDUCE`, etc. -3. **Member Access** - - `.` (dot notation) and `[]` (bracket notation) -4. **Function Calls** - - `()` (function call) -5. **Unary Operators** - - `++`, `--` (pre-increment and pre-decrement) -6. **Exponentiation** - - `**` (power operator) -7. **Multiplicative Operators** - - `*`, `/`, `%` (multiplication, division, modulus) -8. **Additive Operators** - - `+`, `-` (addition and subtraction) -9. **Relational Operators** - - `<`, `<=`, `>`, `>=` (comparison) -10. **Equality Operators** - - `==`, `!=` (equality checks) -11. **Logical Operators** - - `&&`, `||` (logical AND and OR) -12. **Conditional Operator** - - `? :` (ternary operator) -13. **Assignment Operators** - - `=`, `+=`, `-=`, `*=`, `/=`, `%=`, etc. (assignment) +## Operator Precedence Levels + +1. **Primary Expressions** + - Literals, Identifiers, Parenthesized expressions (`parsePrimaryExpr`) + +2. **Short Notation (Data)** + - `@` (short notation for objects and arrays) (`parseShortData`) + +3. **Short Notation (Functions)** + - `MAP`, `FILTER`, `REDUCE`, etc. (`parseCallbackFunctionExpr`) + +4. **Member Access** + - `.` (dot notation), `[]` (bracket notation), `|>` (pipe notation) (`parseMemberExpr`) + +5. **Function Calls** + - `()` (function call) (`parseCallExpr`, `parseCallMemberExpr`) + +6. **Unary Operators** + - `++`, `--` (pre-increment and pre-decrement) + +7. **Exponentiation** + - `**` (power operator) (`parsePowerExpr`) + +8. **Multiplicative Operators** + - `*`, `/`, `%` (multiplication, division, modulus) (`parseMultiplicativeExpr`) + +9. **Additive Operators** + - `+`, `-` (addition and subtraction) (`parseAdditiveExpr`) + +10. **Relational Operators** + - `<`, `<=`, `>`, `>=` (comparison) (`parseRelationalExpr`) + +11. **Equality Operators** + - `==`, `!=` (equality checks) (`parseEqualityExpr`) + +12. **Logical Operators** + - `&&`, `||` (logical AND and OR) (`parseLogicalExpr`) + +13. **Conditional Operator** + - `? :` (ternary operator) (`parseTernaryExpr`) + +14. **Assignment Operators** + - `=`, `+=`, `-=`, `*=`, `/=`, `%=`, etc. (`parseAssignmentExpr`) + +## Parsing Functions Overview + +### Statements +- `parseStatement()` +- `parseStatementBlock()` +- `parseFunctionDeclaration()` +- `parseReturnStatement()` +- `parseVarDeclaration()` +- `parseConditionalStatement()` +- `parseWhileStatement()` +- `parseDoWhileStatement()` +- `parseForStatement()` +- `parseLogStatement()` + +### Expressions +- `parseExpression()` +- `parseAssignmentExpr()` +- `parseTernaryExpr()` +- `parseLogicalExpr()` +- `parseEqualityExpr()` +- `parseRelationalExpr()` +- `parseObjectExpr()` +- `parseArrayExpr()` +- `parseCallbackFunctionExpr()` +- `parseShortData()` +- `parseAdditiveExpr()` +- `parseMultiplicativeExpr()` +- `parsePowerExpr()` +- `parseCallMemberExpr()` +- `parseCallExpr(caller, method)` +- `parseShortExpr(caller, method)` +- `parseArgs()` +- `parseShortArgs()` +- `parseArgumentsList()` +- `parseMemberExpr()` +- `parsePrimaryExpr()` + [Back to Top](#sqeez-parser) \ No newline at end of file diff --git a/include/parser/ast_nodes.hpp b/include/parser/ast_nodes.hpp index 8914205..a55e948 100644 --- a/include/parser/ast_nodes.hpp +++ b/include/parser/ast_nodes.hpp @@ -44,9 +44,7 @@ enum class NodeType { StringLiteral, HexCodeLiteral, // Short Notation - ShortOperationLiteral, - ShortSingleExpressionLiteral, - ShortDoubleExpressionLiteral + ShortOperationLiteral }; // Base class for all AST nodes @@ -371,10 +369,10 @@ class ForOfStmt : public Stmt { class LogStmt : public Stmt { public: Token logType; - std::unique_ptr message; + std::vector> message; std::unique_ptr color; - LogStmt(const Token& logType, std::unique_ptr message, std::unique_ptr color = nullptr) + LogStmt(const Token& logType, std::vector> message, std::unique_ptr color = nullptr) : Stmt(NodeType::LogStmt), logType(logType), message(std::move(message)), color(std::move(color)) {} LogStmt(const LogStmt&) = delete; @@ -383,13 +381,19 @@ class LogStmt : public Stmt { LogStmt& operator=(LogStmt&&) noexcept = default; virtual std::string toString() const override { - std::stringstream ss; - ss << logType.plainText << "(" << message->toString(); + std::string result = "LogStmt: "; + result += logType.value + "("; + for (size_t i = 0; i < message.size(); ++i) { + result += message[i]->toString(); + if (i < message.size() - 1) { + result += ", "; + } + } + result += ")"; if (color) { - ss << ", " << color->toString(); + result += " in " + color->toString(); } - ss << ")"; - return ss.str(); + return result; } }; @@ -768,65 +772,20 @@ class HexCodeLiteral : public Expr { std::string toString() const override { return "HexCodeLiteral: " + value; } }; -// Short Notation class ShortOperationLiteral : public Expr { public: - Token type; - Token operation_; - std::unique_ptr value; + Token operation; + std::unique_ptr operand; - ShortOperationLiteral(const Token& type, const Token& operation_, std::unique_ptr value) - : Expr(NodeType::ShortOperationLiteral), type(type), operation_(operation_), value(std::move(value)) {} + ShortOperationLiteral(const Token& operation, std::unique_ptr operand) + : Expr(NodeType::ShortOperationLiteral), operation(operation), operand(std::move(operand)) {} ShortOperationLiteral(const ShortOperationLiteral&) = delete; ShortOperationLiteral& operator=(const ShortOperationLiteral&) = delete; ShortOperationLiteral(ShortOperationLiteral&&) noexcept = default; - ShortOperationLiteral& operator=(ShortOperationLiteral&&) = default; - - std::string toString() const override { - return "ShortOperationLiteral: " + type.plainText + "(" + operation_.value + value->toString() + ")"; - } -}; - -class ShortSingleExpressionLiteral : public Expr { -public: - Token type; - std::unique_ptr value; - - ShortSingleExpressionLiteral(const Token& type, std::unique_ptr value) - : Expr(NodeType::ShortSingleExpressionLiteral), type(type), value(std::move(value)) {} - - ShortSingleExpressionLiteral(const ShortSingleExpressionLiteral&) = delete; - ShortSingleExpressionLiteral& operator=(const ShortSingleExpressionLiteral&) = delete; - ShortSingleExpressionLiteral(ShortSingleExpressionLiteral&&) noexcept = default; - ShortSingleExpressionLiteral& operator=(ShortSingleExpressionLiteral&&) = default; - - std::string toString() const override { - return "ShortSingleExpressionLiteral: " + type.plainText + "(" + value->toString() + ")"; - } -}; + ShortOperationLiteral& operator=(ShortOperationLiteral&&) noexcept = default; -class ShortDoubleExpressionLiteral : public Expr { -public: - Token type; - std::unique_ptr value1; - std::unique_ptr value2; - - ShortDoubleExpressionLiteral(const Token& type, std::unique_ptr value1, std::unique_ptr value2) - : Expr(NodeType::ShortDoubleExpressionLiteral), - type(type), - value1(std::move(value1)), - value2(std::move(value2)) {} - - ShortDoubleExpressionLiteral(const ShortDoubleExpressionLiteral&) = delete; - ShortDoubleExpressionLiteral& operator=(const ShortDoubleExpressionLiteral&) = delete; - ShortDoubleExpressionLiteral(ShortDoubleExpressionLiteral&&) noexcept = default; - ShortDoubleExpressionLiteral& operator=(ShortDoubleExpressionLiteral&&) = default; - - std::string toString() const override { - return "ShortDoubleExpressionLiteral: " + type.plainText + "(" + value1->toString() + ", " + value2->toString() + - ")"; - } + std::string toString() const override { return "ShortOperationLiteral: " + operation.value + operand->toString(); } }; #endif // AST_NODES_HPP diff --git a/include/parser/parser.hpp b/include/parser/parser.hpp index 07535c7..0a94809 100644 --- a/include/parser/parser.hpp +++ b/include/parser/parser.hpp @@ -37,7 +37,6 @@ class Parser { std::unique_ptr parseLogicalExpr(); std::unique_ptr parseEqualityExpr(); std::unique_ptr parseRelationalExpr(); - std::unique_ptr parsePipeExpr(); std::unique_ptr parseObjectExpr(); std::unique_ptr parseArrayExpr(); std::unique_ptr parseCallbackFunctionExpr(); @@ -47,11 +46,12 @@ class Parser { std::unique_ptr parsePowerExpr(); std::unique_ptr parseCallMemberExpr(); std::unique_ptr parseCallExpr(std::unique_ptr caller, std::unique_ptr method); + std::unique_ptr parseShortExpr(std::unique_ptr caller, std::unique_ptr method); std::vector> parseArgs(); + std::vector> parseShortArgs(); std::vector> parseArgumentsList(); std::unique_ptr parseMemberExpr(); std::unique_ptr parsePrimaryExpr(); - std::unique_ptr parseShortExpr(); // Utility functions bool isEOF(); @@ -62,6 +62,58 @@ class Parser { void log(const std::unique_ptr& program, bool devMode); void skipSemicolon(); void skipComment(); + inline static const std::unordered_map shortEnumToString = { + {ShortNotationToken::LENGTH, "length"}, + {ShortNotationToken::CONCAT, "concat"}, + {ShortNotationToken::INCLUDES, "includes"}, + {ShortNotationToken::INDEX_OF, "indexOf"}, + {ShortNotationToken::LAST_INDEX_OF, "lastIndexOf"}, + {ShortNotationToken::SLICE, "slice"}, + {ShortNotationToken::PUSH, "push"}, + {ShortNotationToken::POP, "pop"}, + {ShortNotationToken::SHIFT, "shift"}, + {ShortNotationToken::UNSHIFT, "unshift"}, + {ShortNotationToken::SPLICE, "splice"}, + {ShortNotationToken::REVERSE, "reverse"}, + {ShortNotationToken::SORT, "sort"}, + {ShortNotationToken::FILL, "fill"}, + {ShortNotationToken::JOIN, "join"}, + {ShortNotationToken::COUNT, "count"}, + {ShortNotationToken::EVERY, "every"}, + {ShortNotationToken::SOME, "some"}, + {ShortNotationToken::FIND, "find"}, + {ShortNotationToken::FIND_INDEX, "findIndex"}, + {ShortNotationToken::FIND_LAST, "findLast"}, + {ShortNotationToken::FIND_LAST_INDEX, "findLastIndex"}, + {ShortNotationToken::FILTER, "filter"}, + {ShortNotationToken::MAP, "map"}, + {ShortNotationToken::REDUCE, "reduce"}, + {ShortNotationToken::FLAT, "flat"}, + {ShortNotationToken::FLAT_MAP, "flatMap"}, + {ShortNotationToken::FOR_EACH, "forEach"}, + {ShortNotationToken::HAS_KEY, "hasKey"}, + {ShortNotationToken::KEYS, "keys"}, + {ShortNotationToken::VALUES, "values"}, + {ShortNotationToken::ENTRIES, "entries"}, + {ShortNotationToken::GET, "get"}, + {ShortNotationToken::CHAR_AT, "charAt"}, + {ShortNotationToken::CHAR_CODE_AT, "charCodeAt"}, + {ShortNotationToken::MATCH, "match"}, + {ShortNotationToken::MATCH_ALL, "matchAll"}, + {ShortNotationToken::PAD_END, "padEnd"}, + {ShortNotationToken::PAD_START, "padStart"}, + {ShortNotationToken::REPEAT, "repeat"}, + {ShortNotationToken::REPLACE, "replace"}, + {ShortNotationToken::REPLACE_ALL, "replaceAll"}, + {ShortNotationToken::SPLIT, "split"}, + {ShortNotationToken::STARTS_WITH, "startsWith"}, + {ShortNotationToken::ENDS_WITH, "endsWith"}, + {ShortNotationToken::SUBSTRING, "substring"}, + {ShortNotationToken::LOWERCASE, "lowercase"}, + {ShortNotationToken::UPPERCASE, "uppercase"}, + {ShortNotationToken::TRIM, "trim"}, + {ShortNotationToken::TRIM_END, "trimEnd"}, + {ShortNotationToken::TRIM_START, "trimStart"}}; }; #endif // PARSER_HPP diff --git a/src/parser/parser.cpp b/src/parser/parser.cpp index 27a36c7..108136f 100644 --- a/src/parser/parser.cpp +++ b/src/parser/parser.cpp @@ -231,18 +231,24 @@ std::unique_ptr Parser::parseForStatement() { std::unique_ptr Parser::parseLogStatement() { Token logType = advance(); // log | logc | warn | error assertToken("SyntaxToken::OPEN_PARENTHESIS", "Expected '(' after log function call."); - - auto messageExpr = parseExpression(); - + std::vector> message = {}; + do { + if (message.size() > 0) { + assertToken("SyntaxToken::COMMA", "Expected ',' between message expressions in log function call."); + } + message.push_back(parseExpression()); + } while (peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::COMMA); std::unique_ptr colorExpr = nullptr; if (logType.tag == Token::TypeTag::LOG && logType.type.logToken == LogToken::COLORED) { - assertToken("SyntaxToken::COMMA", "Expected ',' between message and color in logc."); - colorExpr = parseExpression(); + colorExpr = std::move(message.back()); + message.pop_back(); + if (colorExpr->kind != NodeType::HexCodeLiteral) { + throw std::invalid_argument("Expected hex code literal as last argument for a colored log."); + } } - assertToken("SyntaxToken::CLOSE_PARENTHESIS", "Expected ')' after log function call."); - assertToken("SyntaxToken::SEMICOLON", "Expected ';' after log function call."); - return std::make_unique(logType, std::move(messageExpr), std::move(colorExpr)); + skipSemicolon(); + return std::make_unique(logType, std::move(message), std::move(colorExpr)); } std::unique_ptr Parser::parseExpression() { return parseAssignmentExpr(); } @@ -259,7 +265,7 @@ std::unique_ptr Parser::parseAssignmentExpr() { assertToken("OperatorToken::ASSIGN", "Expected '=' after assignment expression."); value = parseAssignmentExpr(); expression = std::make_unique(std::move(left), std::move(value)); - assertToken("SyntaxToken::SEMICOLON", "Expected ';' after assignment expression."); + skipSemicolon(); return expression; case OperatorToken::ADDITION_ASSIGNMENT: case OperatorToken::SUBTRACTION_ASSIGNMENT: @@ -269,7 +275,7 @@ std::unique_ptr Parser::parseAssignmentExpr() { operator_ = advance(); // + | - | * | / | % value = parseAssignmentExpr(); expression = std::make_unique(std::move(left), std::move(value), operator_); - assertToken("SyntaxToken::SEMICOLON", "Expected ';' after assignment expression."); + skipSemicolon(); return expression; default: break; @@ -319,26 +325,14 @@ std::unique_ptr Parser::parseEqualityExpr() { } std::unique_ptr Parser::parseRelationalExpr() { - auto left = parsePipeExpr(); + auto left = parseAdditiveExpr(); while (peek().tag == Token::TypeTag::LOGICAL && (peek().type.logicalToken == LogicalToken::LESS || peek().type.logicalToken == LogicalToken::GREATER || peek().type.logicalToken == LogicalToken::LESS_EQUAL || peek().type.logicalToken == LogicalToken::GREATER_EQUAL)) { Token operator_ = advance(); // < | > | <= | >= - auto right = parsePipeExpr(); - left = std::make_unique(std::move(left), std::move(right), operator_); - } - - return left; -} - -std::unique_ptr Parser::parsePipeExpr() { - auto left = parseObjectExpr(); - - while (peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::PIPE_OPERATOR) { - Token operator_ = assertToken("SyntaxToken::PIPE_OPERATOR", "Expected pipe operator"); - auto right = parseObjectExpr(); + auto right = parseAdditiveExpr(); left = std::make_unique(std::move(left), std::move(right), operator_); } @@ -346,14 +340,6 @@ std::unique_ptr Parser::parsePipeExpr() { } std::unique_ptr Parser::parseObjectExpr() { - if (peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::AT) { - return parseShortData(); - } - - if (!(peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::OPEN_BRACE)) { - return parseArrayExpr(); - } - assertToken("SyntaxToken::OPEN_BRACE", "Expected opening brace for object"); std::vector> properties; @@ -387,10 +373,6 @@ std::unique_ptr Parser::parseObjectExpr() { } std::unique_ptr Parser::parseArrayExpr() { - if (!(peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::OPEN_BRACKET)) { - return parseAdditiveExpr(); - } - assertToken("SyntaxToken::OPEN_BRACKET", "Expected opening bracket for array"); std::vector> elements; @@ -430,32 +412,29 @@ std::unique_ptr Parser::parseCallbackFunctionExpr() { std::unique_ptr Parser::parseShortData() { assertToken("SyntaxToken::AT", "Expected '@' to start short data notation."); // Short Notation -> Object @ key:value, key:value - if (peek(2).tag == Token::TypeTag::SYNTAX && peek(2).type.syntaxToken == SyntaxToken::COLON) { + if (lookAhead(2).tag == Token::TypeTag::SYNTAX && lookAhead(2).type.syntaxToken == SyntaxToken::COLON) { std::vector> properties; - while (true) { + do { + if (properties.size() > 0) { + assertToken("SyntaxToken::COMMA", "Expected comma between properties in short data notation."); + } Token key = assertToken("DataToken::IDENTIFIER", "Expected identifier key in short data notation."); assertToken("SyntaxToken::COLON", "Expected colon after key in short data notation."); - auto value = parseExpression(); - properties.push_back(std::make_unique(Property{key, std::move(value)})); - if (peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::COMMA) { - assertToken("SyntaxToken::COMMA", "Expected comma after property chain in short data notation."); - } else { - break; - } - } + properties.push_back(std::make_unique(Property{key, std::move(parseExpression())})); + } while (peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::COMMA); + assertToken("SyntaxToken::SEMICOLON", "Expected semicolon after short data notation."); return std::make_unique(std::move(properties)); } // Short Notation -> Array @ value, value, value else { std::vector> elements; - while (true) { - elements.push_back(parseExpression()); - if (peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::COMMA) { - assertToken("SyntaxToken::COMMA", "Expected comma after element chain in short data notation."); - } else { - break; + do { + if (elements.size() > 0) { + assertToken("SyntaxToken::COMMA", "Expected comma between elements in short data notation."); } - } + elements.push_back(parseExpression()); + } while (peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::COMMA); + assertToken("SyntaxToken::SEMICOLON", "Expected semicolon after short data notation."); return std::make_unique(std::move(elements)); } } @@ -522,10 +501,36 @@ std::unique_ptr Parser::parseCallExpr(std::unique_ptr caller, std::u callExpr->args = parseArgs(); if (peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::DOT) { - assertToken("SyntaxToken::DOT", "Expected dot operator for method chaining"); + assertToken("SyntaxToken::DOT", "Expected dot operator following method call"); auto followingMethod = parsePrimaryExpr(); callExpr = std::unique_ptr( static_cast(parseCallExpr(std::move(callExpr), std::move(followingMethod)).release())); + } else if (peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::PIPE_OPERATOR) { + assertToken("SyntaxToken::PIPE_OPERATOR", "Expected pipe operator following method call"); + auto followingMethod = parsePrimaryExpr(); + callExpr = std::unique_ptr( + static_cast(parseShortExpr(std::move(callExpr), std::move(followingMethod)).release())); + } + + return callExpr; +} + +std::unique_ptr Parser::parseShortExpr(std::unique_ptr caller, std::unique_ptr method) { + auto callExpr = std::make_unique(); + callExpr->caller = std::move(caller); + callExpr->method = std::move(method); + callExpr->args = parseShortArgs(); + + if (peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::DOT) { + assertToken("SyntaxToken::DOT", "Expected dot operator following method call"); + auto followingMethod = parsePrimaryExpr(); + callExpr = std::unique_ptr( + static_cast(parseCallExpr(std::move(callExpr), std::move(followingMethod)).release())); + } else if (peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::PIPE_OPERATOR) { + assertToken("SyntaxToken::PIPE_OPERATOR", "Expected pipe operator following method call"); + auto followingMethod = parsePrimaryExpr(); + callExpr = std::unique_ptr( + static_cast(parseShortExpr(std::move(callExpr), std::move(followingMethod)).release())); } return callExpr; @@ -543,6 +548,34 @@ std::vector> Parser::parseArgs() { return args; } +std::vector> Parser::parseShortArgs() { + assertToken("SyntaxToken::OPEN_PARENTHESIS", "Expected open parenthesis"); + std::vector> args; + + while (peek().tag != Token::TypeTag::SYNTAX && peek().type.syntaxToken != SyntaxToken::CLOSE_PARENTHESIS) { + if (args.size() > 0) { + assertToken("SyntaxToken::COMMA", "Expected comma between arguments"); + } + if ((peek().tag == Token::TypeTag::LOGICAL && + !(peek().type.logicalToken == LogicalToken::AND || peek().type.logicalToken == LogicalToken::OR)) || + (peek().tag == Token::TypeTag::OPERATOR && + !(peek().type.operatorToken == OperatorToken::ADDITION_ASSIGNMENT || + peek().type.operatorToken == OperatorToken::SUBTRACTION_ASSIGNMENT || + peek().type.operatorToken == OperatorToken::MULTIPLICATION_ASSIGNMENT || + peek().type.operatorToken == OperatorToken::DIVISION_ASSIGNMENT || + peek().type.operatorToken == OperatorToken::MODULUS_ASSIGNMENT || + peek().type.operatorToken == OperatorToken::POTENTIATION_ASSIGNMENT))) { + args.push_back(std::make_unique(ShortOperationLiteral{advance(), parseExpression()})); + } else if (peek().tag == Token::TypeTag::LOG) { + args.push_back(std::unique_ptr(static_cast(parseLogStatement().release()))); + } else { + args.push_back(parseExpression()); + } + } + assertToken("SyntaxToken::CLOSE_PARENTHESIS", "Missing closing parenthesis inside arguments list"); + return args; +} + std::vector> Parser::parseArgumentsList() { std::vector> args; args.push_back(parseAssignmentExpr()); @@ -559,14 +592,13 @@ std::unique_ptr Parser::parseMemberExpr() { std::unique_ptr object = parsePrimaryExpr(); while ((peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::DOT) || - (peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::OPEN_BRACKET)) { - Token operatorToken = advance(); // . | [ + (peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::OPEN_BRACKET) || + (peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::PIPE_OPERATOR)) { + Token token = advance(); // . | [ | |> std::unique_ptr property; - bool computed; // dot notation - if (operatorToken.type.syntaxToken == SyntaxToken::DOT) { - computed = false; + if (token.type.syntaxToken == SyntaxToken::DOT) { property = parsePrimaryExpr(); if (property->kind != NodeType::Identifier) { throw std::invalid_argument("Cannot use dot operator without right-hand side being an identifier."); @@ -575,15 +607,22 @@ std::unique_ptr Parser::parseMemberExpr() { if (peek().tag == Token::TypeTag::SYNTAX && peek().type.syntaxToken == SyntaxToken::OPEN_PARENTHESIS) { object = parseCallExpr(std::move(object), std::move(property)); } else { - object = std::make_unique(MemberExpr{std::move(object), std::move(property), computed}); + object = std::make_unique(MemberExpr{std::move(object), std::move(property), false}); } } // bracket notation - else { - computed = true; + else if (token.type.syntaxToken == SyntaxToken::OPEN_BRACKET) { property = parseExpression(); assertToken("SyntaxToken::CLOSE_BRACKET", "Missing closing bracket in computed value."); - object = std::make_unique(MemberExpr{std::move(object), std::move(property), computed}); + object = std::make_unique(MemberExpr{std::move(object), std::move(property), true}); + } + // pipe operator + else { + if (peek().tag != Token::TypeTag::SHORT_NOTATION) { + throw std::invalid_argument("Expected Short Notation following pipe operator."); + } + property = parsePrimaryExpr(); + object = parseShortExpr(std::move(object), std::move(property)); } } @@ -622,6 +661,12 @@ std::unique_ptr Parser::parsePrimaryExpr() { std::string value; int i, scope = 0; switch (token.type.syntaxToken) { + case SyntaxToken::OPEN_BRACKET: + return parseArrayExpr(); + case SyntaxToken::OPEN_BRACE: + return parseObjectExpr(); + case SyntaxToken::AT: + return parseShortData(); case SyntaxToken::OPEN_PARENTHESIS: assertToken("SyntaxToken::OPEN_PARENTHESIS", "Expected '(' to start parenthesised expression."); while (true) { @@ -695,72 +740,12 @@ std::unique_ptr Parser::parsePrimaryExpr() { break; } } else if (token.tag == Token::TypeTag::SHORT_NOTATION) { - return parseShortExpr(); + token.value = shortEnumToString.at(advance().type.shortNotationToken); + return std::make_unique(Identifier{token}); } throw std::logic_error("Unexpected token \"" + token.plainText + "\" found in primary expression."); } -std::unique_ptr Parser::parseShortExpr() { - Token shortKey = advance(); // MAP | REDUCE | FILTER | CONCAT | ZIP | JOIN | FIND | COUNT | SORT | REVERSE - assertToken("SyntaxToken::OPEN_PARENTHESIS", "Expected '(' after short expression type."); - Token operation_ = peek(); - std::unique_ptr value; - std::unique_ptr value2; - std::unique_ptr expression; - switch (shortKey.type.shortNotationToken) { - case ShortNotationToken::MAP: - case ShortNotationToken::REDUCE: - operation_ = advance(); // + | - | * | / | % - if (!(operation_.tag == Token::TypeTag::OPERATOR && - (operation_.type.operatorToken == OperatorToken::ADDITION || - operation_.type.operatorToken == OperatorToken::SUBTRACTION || - operation_.type.operatorToken == OperatorToken::MULTIPLICATION || - operation_.type.operatorToken == OperatorToken::DIVISION || - operation_.type.operatorToken == OperatorToken::MODULUS))) { - throw std::invalid_argument("Unexpected operation found in short expression " + shortKey.plainText + ": " + - operation_.plainText); - } - value = parseExpression(); - expression = std::make_unique(shortKey, operation_, std::move(value)); - break; - case ShortNotationToken::FILTER: - operation_ = advance(); // == | != | < | > | <= | >= - if (!(operation_.tag == Token::TypeTag::LOGICAL) && - (operation_.type.logicalToken == LogicalToken::EQUAL || - operation_.type.logicalToken == LogicalToken::NOT_EQUAL || - operation_.type.logicalToken == LogicalToken::LESS || - operation_.type.logicalToken == LogicalToken::GREATER || - operation_.type.logicalToken == LogicalToken::LESS_EQUAL || - operation_.type.logicalToken == LogicalToken::GREATER_EQUAL)) { - throw std::invalid_argument("Unexpected operation found in short expression " + shortKey.plainText + ": " + - operation_.plainText); - } - value = parseExpression(); - expression = std::make_unique(shortKey, operation_, std::move(value)); - break; - case ShortNotationToken::CONCAT: - case ShortNotationToken::ZIP: - case ShortNotationToken::JOIN: - case ShortNotationToken::FIND: - case ShortNotationToken::COUNT: - value = parseExpression(); - assertToken("SyntaxToken::COMMA", "Expected ',' between values in short expression."); - value2 = parseExpression(); - expression = std::make_unique(shortKey, std::move(value), std::move(value2)); - break; - case ShortNotationToken::SORT: - case ShortNotationToken::REVERSE: - value = parseExpression(); - expression = std::make_unique(shortKey, std::move(value)); - break; - default: - throw std::invalid_argument("Unexpected short expression type: " + shortKey.plainText); - break; - } - assertToken("SyntaxToken::CLOSE_PARENTHESIS", "Expected ')' after short expression content."); - return expression; -} - // Utility functions bool Parser::isEOF() { return peek().tag == Token::TypeTag::BASIC && peek().type.basicToken == BasicToken::TOKEN_EOF; } @@ -811,4 +796,4 @@ void Parser::skipComment() { if (peek().tag == Token::TypeTag::DATA && peek().type.dataToken == DataToken::COMMENT_LITERAL) { assertToken("DataToken::COMMENT_LITERAL", "Expected comment literal"); } -} +} \ No newline at end of file