natalie_parser 1.2.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -0
- data/include/natalie_parser/node/colon2_node.hpp +1 -0
- data/include/natalie_parser/node/for_node.hpp +50 -0
- data/include/natalie_parser/node/hash_node.hpp +8 -3
- data/include/natalie_parser/node/hash_pattern_node.hpp +1 -1
- data/include/natalie_parser/node/match_node.hpp +6 -0
- data/include/natalie_parser/node/node.hpp +1 -0
- data/include/natalie_parser/node/unary_op_node.hpp +1 -1
- data/include/natalie_parser/node.hpp +1 -0
- data/include/natalie_parser/parser.hpp +3 -1
- data/include/natalie_parser/token.hpp +18 -0
- data/lib/natalie_parser/version.rb +1 -1
- data/src/lexer.cpp +5 -1
- data/src/node/match_node.cpp +5 -0
- data/src/parser.cpp +86 -35
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0598d42a8e0b83aa5a340d1615474a4c5a8e7b209032bf574a230778e4d3ce5e'
|
4
|
+
data.tar.gz: 475cce9e2f078ab440674b48e3b99ba48f163f34b889b376cf681f74a489ce53
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59cd20f85484845036f934ca5b40c020ad35655824950c036a697bf0bc437d3ebd35eb25bacc7c6f062d096c01b58dea5a1ebd688d0d101192ee7f43b9881b25
|
7
|
+
data.tar.gz: 53bcc9b34ab02943dda1602141ae949e4fa1dcf1c229dfeda8ef710b01d1d9d9c97137ce7e5b08e88b121025009c9a9fd71edbc77eead37f69f37b794fab02f8
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,28 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 2.1.0 (2022-08-12)
|
4
|
+
|
5
|
+
- FEAT: Parse for loops
|
6
|
+
- FIX: Fix bug parsing defined? with parens
|
7
|
+
- FIX: Fix parsing of keyword splat next to other keyword args
|
8
|
+
- FIX: Parse block pass after bare/implicit hash
|
9
|
+
- FIX: Parse if statements with match conditions
|
10
|
+
- FIX: Parse regexps with leading space preceeded by keywords
|
11
|
+
- FIX: Parse symbol key after super keyword
|
12
|
+
- FIX: Parse unless statements with match conditions
|
13
|
+
- FIX: Parse while/until statements with match conditions
|
14
|
+
- FIX: Reset block association level inside array
|
15
|
+
|
16
|
+
## 2.0.0 (2022-06-24)
|
17
|
+
|
18
|
+
- FEAT: Differentiate between bare/implicit hash and explicit one
|
19
|
+
- FIX: Fix calling colon2
|
20
|
+
- FIX: Parse implicit method calls with nth ref argument
|
21
|
+
|
22
|
+
## 1.2.1 (2022-06-16)
|
23
|
+
|
24
|
+
- FIX: Fix regression with unary/infix operators (+/-)
|
25
|
+
|
3
26
|
## 1.2.0 (2022-06-16)
|
4
27
|
|
5
28
|
- CHORE: Enable true random fuzzing
|
@@ -23,6 +23,7 @@ public:
|
|
23
23
|
virtual Type type() const override { return Type::Colon2; }
|
24
24
|
|
25
25
|
virtual bool is_assignable() const override { return true; }
|
26
|
+
virtual bool is_callable() const override { return true; }
|
26
27
|
|
27
28
|
const SharedPtr<Node> left() const { return m_left; }
|
28
29
|
SharedPtr<String> name() const { return m_name; }
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include "natalie_parser/node/block_node.hpp"
|
4
|
+
#include "natalie_parser/node/node.hpp"
|
5
|
+
#include "natalie_parser/node/node_with_args.hpp"
|
6
|
+
|
7
|
+
namespace NatalieParser {
|
8
|
+
|
9
|
+
using namespace TM;
|
10
|
+
|
11
|
+
class ForNode : public Node {
|
12
|
+
public:
|
13
|
+
ForNode(const Token &token, SharedPtr<Node> expr, SharedPtr<Node> vars, SharedPtr<BlockNode> body)
|
14
|
+
: Node { token }
|
15
|
+
, m_expr { expr }
|
16
|
+
, m_vars { vars }
|
17
|
+
, m_body { body } {
|
18
|
+
assert(m_expr);
|
19
|
+
assert(m_vars);
|
20
|
+
}
|
21
|
+
|
22
|
+
virtual Type type() const override { return Type::For; }
|
23
|
+
|
24
|
+
const SharedPtr<Node> expr() const { return m_expr; }
|
25
|
+
const SharedPtr<Node> vars() const { return m_vars; }
|
26
|
+
const SharedPtr<BlockNode> body() const { return m_body; }
|
27
|
+
|
28
|
+
virtual void transform(Creator *creator) const override {
|
29
|
+
creator->set_type("for");
|
30
|
+
creator->append(m_expr);
|
31
|
+
switch (m_vars->type()) {
|
32
|
+
case Node::Type::Identifier:
|
33
|
+
creator->with_assignment(true, [&]() { creator->append(*m_vars); });
|
34
|
+
break;
|
35
|
+
case Node::Type::MultipleAssignment:
|
36
|
+
creator->append(m_vars);
|
37
|
+
break;
|
38
|
+
default:
|
39
|
+
TM_UNREACHABLE();
|
40
|
+
}
|
41
|
+
if (!m_body->is_empty())
|
42
|
+
creator->append(m_body->without_unnecessary_nesting());
|
43
|
+
}
|
44
|
+
|
45
|
+
protected:
|
46
|
+
SharedPtr<Node> m_expr {};
|
47
|
+
SharedPtr<Node> m_vars {};
|
48
|
+
SharedPtr<BlockNode> m_body {};
|
49
|
+
};
|
50
|
+
}
|
@@ -11,8 +11,9 @@ using namespace TM;
|
|
11
11
|
|
12
12
|
class HashNode : public Node {
|
13
13
|
public:
|
14
|
-
HashNode(const Token &token)
|
15
|
-
: Node { token }
|
14
|
+
HashNode(const Token &token, bool bare)
|
15
|
+
: Node { token }
|
16
|
+
, m_bare { bare } { }
|
16
17
|
|
17
18
|
virtual Type type() const override { return Type::Hash; }
|
18
19
|
|
@@ -23,12 +24,16 @@ public:
|
|
23
24
|
const Vector<SharedPtr<Node>> &nodes() const { return m_nodes; }
|
24
25
|
|
25
26
|
virtual void transform(Creator *creator) const override {
|
26
|
-
|
27
|
+
if (m_bare)
|
28
|
+
creator->set_type("bare_hash");
|
29
|
+
else
|
30
|
+
creator->set_type("hash");
|
27
31
|
for (auto node : m_nodes)
|
28
32
|
creator->append(node);
|
29
33
|
}
|
30
34
|
|
31
35
|
protected:
|
32
36
|
Vector<SharedPtr<Node>> m_nodes {};
|
37
|
+
bool m_bare { false };
|
33
38
|
};
|
34
39
|
}
|
@@ -13,6 +13,12 @@ using namespace TM;
|
|
13
13
|
|
14
14
|
class MatchNode : public Node {
|
15
15
|
public:
|
16
|
+
MatchNode(const Token &token, SharedPtr<RegexpNode> regexp)
|
17
|
+
: Node { token }
|
18
|
+
, m_regexp { regexp } {
|
19
|
+
assert(m_regexp);
|
20
|
+
}
|
21
|
+
|
16
22
|
MatchNode(const Token &token, SharedPtr<RegexpNode> regexp, SharedPtr<Node> arg, bool regexp_on_left)
|
17
23
|
: Node { token }
|
18
24
|
, m_regexp { regexp }
|
@@ -20,7 +20,7 @@ public:
|
|
20
20
|
|
21
21
|
virtual Type type() const override { return Type::UnaryOp; }
|
22
22
|
|
23
|
-
virtual bool is_callable() const override { return
|
23
|
+
virtual bool is_callable() const override { return false; }
|
24
24
|
virtual bool can_accept_a_block() const override { return false; }
|
25
25
|
|
26
26
|
const SharedPtr<String> op() const { return m_op; }
|
@@ -31,6 +31,7 @@
|
|
31
31
|
#include "natalie_parser/node/false_node.hpp"
|
32
32
|
#include "natalie_parser/node/fixnum_node.hpp"
|
33
33
|
#include "natalie_parser/node/float_node.hpp"
|
34
|
+
#include "natalie_parser/node/for_node.hpp"
|
34
35
|
#include "natalie_parser/node/forward_args_node.hpp"
|
35
36
|
#include "natalie_parser/node/hash_node.hpp"
|
36
37
|
#include "natalie_parser/node/hash_pattern_node.hpp"
|
@@ -101,12 +101,14 @@ private:
|
|
101
101
|
SharedPtr<Node> parse_encoding(LocalsHashmap &);
|
102
102
|
SharedPtr<Node> parse_end_block(LocalsHashmap &);
|
103
103
|
SharedPtr<Node> parse_file_constant(LocalsHashmap &);
|
104
|
+
SharedPtr<Node> parse_for(LocalsHashmap &);
|
104
105
|
SharedPtr<Node> parse_forward_args(LocalsHashmap &);
|
105
106
|
SharedPtr<Node> parse_group(LocalsHashmap &);
|
106
107
|
SharedPtr<Node> parse_hash(LocalsHashmap &);
|
107
|
-
SharedPtr<Node> parse_hash_inner(LocalsHashmap &, Precedence, Token::Type, SharedPtr<Node> = {});
|
108
|
+
SharedPtr<Node> parse_hash_inner(LocalsHashmap &, Precedence, Token::Type, bool, SharedPtr<Node> = {});
|
108
109
|
SharedPtr<Node> parse_identifier(LocalsHashmap &);
|
109
110
|
SharedPtr<Node> parse_if(LocalsHashmap &);
|
111
|
+
SharedPtr<Node> parse_if_branch(LocalsHashmap &, bool);
|
110
112
|
void parse_interpolated_body(LocalsHashmap &, InterpolatedNode &, Token::Type);
|
111
113
|
SharedPtr<Node> parse_interpolated_regexp(LocalsHashmap &);
|
112
114
|
int parse_regexp_options(String &);
|
@@ -775,6 +775,7 @@ public:
|
|
775
775
|
case Token::Type::NilKeyword:
|
776
776
|
case Token::Type::Not:
|
777
777
|
case Token::Type::NotKeyword:
|
778
|
+
case Token::Type::NthRef:
|
778
779
|
case Token::Type::PercentLowerI:
|
779
780
|
case Token::Type::PercentLowerW:
|
780
781
|
case Token::Type::PercentUpperI:
|
@@ -807,6 +808,22 @@ public:
|
|
807
808
|
}
|
808
809
|
}
|
809
810
|
|
811
|
+
bool can_precede_regexp_literal() const {
|
812
|
+
switch (m_type) {
|
813
|
+
case Type::ElsifKeyword:
|
814
|
+
case Type::IfKeyword:
|
815
|
+
case Type::RescueKeyword:
|
816
|
+
case Type::ReturnKeyword:
|
817
|
+
case Type::UnlessKeyword:
|
818
|
+
case Type::UntilKeyword:
|
819
|
+
case Type::WhenKeyword:
|
820
|
+
case Type::WhileKeyword:
|
821
|
+
return true;
|
822
|
+
default:
|
823
|
+
return false;
|
824
|
+
}
|
825
|
+
}
|
826
|
+
|
810
827
|
bool can_precede_symbol_key() const {
|
811
828
|
switch (m_type) {
|
812
829
|
case Type::BareName:
|
@@ -817,6 +834,7 @@ public:
|
|
817
834
|
case Type::LParen:
|
818
835
|
case Type::Pipe:
|
819
836
|
case Type::PipePipe:
|
837
|
+
case Type::SuperKeyword:
|
820
838
|
return true;
|
821
839
|
default:
|
822
840
|
return false;
|
data/src/lexer.cpp
CHANGED
@@ -270,7 +270,11 @@ Token Lexer::build_next_token() {
|
|
270
270
|
default: {
|
271
271
|
switch (current_char()) {
|
272
272
|
case ' ':
|
273
|
-
|
273
|
+
if (m_last_token.is_keyword() && m_last_token.can_precede_regexp_literal()) {
|
274
|
+
return consume_regexp('/', '/');
|
275
|
+
} else {
|
276
|
+
return Token { Token::Type::Slash, m_file, m_token_line, m_token_column, m_whitespace_precedes };
|
277
|
+
}
|
274
278
|
case '=':
|
275
279
|
advance();
|
276
280
|
return Token { Token::Type::SlashEqual, m_file, m_token_line, m_token_column, m_whitespace_precedes };
|
data/src/node/match_node.cpp
CHANGED
data/src/parser.cpp
CHANGED
@@ -337,7 +337,7 @@ SharedPtr<Node> Parser::parse_array(LocalsHashmap &locals) {
|
|
337
337
|
return array.static_cast_as<Node>();
|
338
338
|
}
|
339
339
|
if (token.type() == Token::Type::SymbolKey) {
|
340
|
-
array->add_node(parse_hash_inner(locals, Precedence::HASH, Token::Type::RBracket));
|
340
|
+
array->add_node(parse_hash_inner(locals, Precedence::HASH, Token::Type::RBracket, true));
|
341
341
|
expect(Token::Type::RBracket, "array closing bracket");
|
342
342
|
advance();
|
343
343
|
return array.static_cast_as<Node>();
|
@@ -345,7 +345,7 @@ SharedPtr<Node> Parser::parse_array(LocalsHashmap &locals) {
|
|
345
345
|
auto value = parse_expression(Precedence::ARRAY, locals);
|
346
346
|
token = current_token();
|
347
347
|
if (token.is_hash_rocket()) {
|
348
|
-
array->add_node(parse_hash_inner(locals, Precedence::HASH, Token::Type::RBracket, value));
|
348
|
+
array->add_node(parse_hash_inner(locals, Precedence::HASH, Token::Type::RBracket, true, value));
|
349
349
|
expect(Token::Type::RBracket, "array closing bracket");
|
350
350
|
advance();
|
351
351
|
return array.static_cast_as<Node>();
|
@@ -354,11 +354,17 @@ SharedPtr<Node> Parser::parse_array(LocalsHashmap &locals) {
|
|
354
354
|
return {};
|
355
355
|
};
|
356
356
|
auto ret = add_node();
|
357
|
-
if (ret)
|
357
|
+
if (ret) {
|
358
|
+
m_call_depth.pop();
|
359
|
+
return ret;
|
360
|
+
}
|
358
361
|
while (current_token().is_comma()) {
|
359
362
|
advance();
|
360
363
|
ret = add_node();
|
361
|
-
if (ret)
|
364
|
+
if (ret) {
|
365
|
+
m_call_depth.pop();
|
366
|
+
return ret;
|
367
|
+
}
|
362
368
|
}
|
363
369
|
expect(Token::Type::RBracket, "array closing bracket");
|
364
370
|
advance(); // ]
|
@@ -1058,7 +1064,16 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
|
|
1058
1064
|
SharedPtr<Node> Parser::parse_defined(LocalsHashmap &locals) {
|
1059
1065
|
auto token = current_token();
|
1060
1066
|
advance();
|
1061
|
-
|
1067
|
+
bool bare = true;
|
1068
|
+
if (current_token().is_lparen()) {
|
1069
|
+
advance();
|
1070
|
+
bare = false;
|
1071
|
+
}
|
1072
|
+
auto arg = parse_expression(bare ? Precedence::BARE_CALL_ARG : Precedence::CALL_ARG, locals);
|
1073
|
+
if (!bare) {
|
1074
|
+
expect(Token::Type::RParen, "defined? closing paren");
|
1075
|
+
advance();
|
1076
|
+
}
|
1062
1077
|
return new DefinedNode { token, arg };
|
1063
1078
|
}
|
1064
1079
|
|
@@ -1248,6 +1263,22 @@ SharedPtr<Node> Parser::parse_file_constant(LocalsHashmap &) {
|
|
1248
1263
|
return new StringNode { token, token.file() };
|
1249
1264
|
}
|
1250
1265
|
|
1266
|
+
SharedPtr<Node> Parser::parse_for(LocalsHashmap &locals) {
|
1267
|
+
auto token = current_token();
|
1268
|
+
advance();
|
1269
|
+
auto vars = parse_assignment_identifier(false, locals);
|
1270
|
+
if (current_token().type() == Token::Type::Comma) {
|
1271
|
+
vars = parse_multiple_assignment_expression(vars, locals);
|
1272
|
+
}
|
1273
|
+
expect(Token::Type::InKeyword, "for in");
|
1274
|
+
advance();
|
1275
|
+
auto expr = parse_expression(Precedence::LOWEST, locals);
|
1276
|
+
auto body = parse_body(locals, Precedence::LOWEST);
|
1277
|
+
expect(Token::Type::EndKeyword, "for end");
|
1278
|
+
advance();
|
1279
|
+
return new ForNode { token, expr, vars, body };
|
1280
|
+
}
|
1281
|
+
|
1251
1282
|
SharedPtr<Node> Parser::parse_forward_args(LocalsHashmap &locals) {
|
1252
1283
|
auto token = current_token();
|
1253
1284
|
if (!locals.get("..."))
|
@@ -1299,39 +1330,46 @@ SharedPtr<Node> Parser::parse_hash(LocalsHashmap &locals) {
|
|
1299
1330
|
advance();
|
1300
1331
|
SharedPtr<Node> hash;
|
1301
1332
|
if (current_token().type() == Token::Type::RCurlyBrace)
|
1302
|
-
hash = new HashNode { token };
|
1333
|
+
hash = new HashNode { token, false };
|
1303
1334
|
else
|
1304
|
-
hash = parse_hash_inner(locals, Precedence::HASH, Token::Type::RCurlyBrace);
|
1335
|
+
hash = parse_hash_inner(locals, Precedence::HASH, Token::Type::RCurlyBrace, false);
|
1305
1336
|
expect(Token::Type::RCurlyBrace, "hash closing curly brace");
|
1306
1337
|
advance();
|
1307
1338
|
return hash;
|
1308
1339
|
}
|
1309
1340
|
|
1310
|
-
SharedPtr<Node> Parser::parse_hash_inner(LocalsHashmap &locals, Precedence precedence, Token::Type closing_token_type, SharedPtr<Node> first_key) {
|
1341
|
+
SharedPtr<Node> Parser::parse_hash_inner(LocalsHashmap &locals, Precedence precedence, Token::Type closing_token_type, bool bare, SharedPtr<Node> first_key) {
|
1311
1342
|
auto token = current_token();
|
1312
|
-
SharedPtr<HashNode> hash = new HashNode { token };
|
1343
|
+
SharedPtr<HashNode> hash = new HashNode { token, bare };
|
1344
|
+
|
1345
|
+
auto add_value = [&](SharedPtr<Node> key) {
|
1346
|
+
if (key->is_symbol_key()) {
|
1347
|
+
hash->add_node(parse_expression(precedence, locals));
|
1348
|
+
} else if (key->type() == Node::Type::KeywordSplat) {
|
1349
|
+
// kwsplat already added
|
1350
|
+
} else {
|
1351
|
+
expect(Token::Type::HashRocket, "hash rocket");
|
1352
|
+
advance(); // =>
|
1353
|
+
hash->add_node(parse_expression(precedence, locals));
|
1354
|
+
}
|
1355
|
+
};
|
1356
|
+
|
1313
1357
|
if (!first_key)
|
1314
1358
|
first_key = parse_expression(precedence, locals);
|
1315
1359
|
hash->add_node(first_key);
|
1316
|
-
|
1317
|
-
|
1318
|
-
advance();
|
1319
|
-
}
|
1320
|
-
hash->add_node(parse_expression(precedence, locals));
|
1360
|
+
add_value(first_key);
|
1361
|
+
|
1321
1362
|
while (current_token().is_comma()) {
|
1322
1363
|
advance();
|
1323
1364
|
if (current_token().type() == closing_token_type)
|
1324
1365
|
break;
|
1325
|
-
if (current_token().type() == Token::Type::
|
1366
|
+
if (current_token().type() == Token::Type::Ampersand) // &block
|
1326
1367
|
break;
|
1327
1368
|
auto key = parse_expression(precedence, locals);
|
1328
1369
|
hash->add_node(key);
|
1329
|
-
|
1330
|
-
expect(Token::Type::HashRocket, "hash rocket");
|
1331
|
-
advance();
|
1332
|
-
}
|
1333
|
-
hash->add_node(parse_expression(precedence, locals));
|
1370
|
+
add_value(key);
|
1334
1371
|
}
|
1372
|
+
|
1335
1373
|
return hash.static_cast_as<Node>();
|
1336
1374
|
}
|
1337
1375
|
|
@@ -1345,9 +1383,16 @@ SharedPtr<Node> Parser::parse_identifier(LocalsHashmap &locals) {
|
|
1345
1383
|
};
|
1346
1384
|
|
1347
1385
|
SharedPtr<Node> Parser::parse_if(LocalsHashmap &locals) {
|
1386
|
+
return parse_if_branch(locals, true);
|
1387
|
+
}
|
1388
|
+
|
1389
|
+
SharedPtr<Node> Parser::parse_if_branch(LocalsHashmap &locals, bool parse_match_condition) {
|
1348
1390
|
auto token = current_token();
|
1349
1391
|
advance();
|
1350
1392
|
SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals);
|
1393
|
+
if (parse_match_condition && condition->type() == Node::Type::Regexp) {
|
1394
|
+
condition = new MatchNode { condition->token(), condition.static_cast_as<RegexpNode>() };
|
1395
|
+
}
|
1351
1396
|
if (current_token().type() == Token::Type::ThenKeyword) {
|
1352
1397
|
advance(); // then
|
1353
1398
|
} else {
|
@@ -1356,7 +1401,7 @@ SharedPtr<Node> Parser::parse_if(LocalsHashmap &locals) {
|
|
1356
1401
|
SharedPtr<Node> true_expr = parse_if_body(locals);
|
1357
1402
|
SharedPtr<Node> false_expr;
|
1358
1403
|
if (current_token().is_elsif_keyword()) {
|
1359
|
-
false_expr =
|
1404
|
+
false_expr = parse_if_branch(locals, false);
|
1360
1405
|
return new IfNode { current_token(), condition, true_expr, false_expr };
|
1361
1406
|
} else {
|
1362
1407
|
if (current_token().is_else_keyword()) {
|
@@ -1639,12 +1684,6 @@ SharedPtr<Node> Parser::parse_keyword_splat(LocalsHashmap &locals) {
|
|
1639
1684
|
return new KeywordSplatNode { token, parse_expression(Precedence::SPLAT, locals) };
|
1640
1685
|
}
|
1641
1686
|
|
1642
|
-
SharedPtr<Node> Parser::parse_keyword_splat_wrapped_in_hash(LocalsHashmap &locals) {
|
1643
|
-
SharedPtr<HashNode> hash = new HashNode { current_token() };
|
1644
|
-
hash->add_node(parse_keyword_splat(locals));
|
1645
|
-
return hash.static_cast_as<Node>();
|
1646
|
-
}
|
1647
|
-
|
1648
1687
|
SharedPtr<String> Parser::parse_method_name(LocalsHashmap &) {
|
1649
1688
|
SharedPtr<String> name = new String("");
|
1650
1689
|
auto token = current_token();
|
@@ -2313,7 +2352,7 @@ void Parser::parse_call_args(NodeWithArgs &node, LocalsHashmap &locals, bool bar
|
|
2313
2352
|
if (bare && node.can_accept_a_block())
|
2314
2353
|
m_call_depth.last()++;
|
2315
2354
|
auto arg = parse_expression(bare ? Precedence::BARE_CALL_ARG : Precedence::CALL_ARG, locals);
|
2316
|
-
if (current_token().is_hash_rocket() || arg->is_symbol_key()) {
|
2355
|
+
if (current_token().is_hash_rocket() || arg->is_symbol_key() || arg->type() == Node::Type::KeywordSplat) {
|
2317
2356
|
node.add_arg(parse_call_hash_args(locals, bare, closing_token_type, arg));
|
2318
2357
|
} else {
|
2319
2358
|
node.add_arg(arg);
|
@@ -2325,7 +2364,7 @@ void Parser::parse_call_args(NodeWithArgs &node, LocalsHashmap &locals, bool bar
|
|
2325
2364
|
break;
|
2326
2365
|
}
|
2327
2366
|
arg = parse_expression(bare ? Precedence::BARE_CALL_ARG : Precedence::CALL_ARG, locals);
|
2328
|
-
if (current_token().is_hash_rocket() || arg->is_symbol_key()) {
|
2367
|
+
if (current_token().is_hash_rocket() || arg->is_symbol_key() || arg->type() == Node::Type::KeywordSplat) {
|
2329
2368
|
node.add_arg(parse_call_hash_args(locals, bare, closing_token_type, arg));
|
2330
2369
|
break;
|
2331
2370
|
} else {
|
@@ -2333,16 +2372,20 @@ void Parser::parse_call_args(NodeWithArgs &node, LocalsHashmap &locals, bool bar
|
|
2333
2372
|
}
|
2334
2373
|
}
|
2335
2374
|
}
|
2375
|
+
if (current_token().type() == Token::Type::Ampersand) {
|
2376
|
+
node.add_arg(parse_block_pass(locals));
|
2377
|
+
}
|
2336
2378
|
if (bare && node.can_accept_a_block())
|
2337
2379
|
m_call_depth.last()--;
|
2338
2380
|
}
|
2339
2381
|
|
2340
|
-
SharedPtr<Node> Parser::parse_call_hash_args(LocalsHashmap &locals, bool
|
2382
|
+
SharedPtr<Node> Parser::parse_call_hash_args(LocalsHashmap &locals, bool bare_call, Token::Type closing_token_type, SharedPtr<Node> first_arg) {
|
2383
|
+
bool bare_hash = true; // we got here via foo(1, a: 'b') so it's always a "bare" hash
|
2341
2384
|
SharedPtr<Node> hash;
|
2342
|
-
if (
|
2343
|
-
hash = parse_hash_inner(locals, Precedence::BARE_CALL_ARG, closing_token_type, first_arg);
|
2385
|
+
if (bare_call)
|
2386
|
+
hash = parse_hash_inner(locals, Precedence::BARE_CALL_ARG, closing_token_type, bare_hash, first_arg);
|
2344
2387
|
else
|
2345
|
-
hash = parse_hash_inner(locals, Precedence::CALL_ARG, closing_token_type, first_arg);
|
2388
|
+
hash = parse_hash_inner(locals, Precedence::CALL_ARG, closing_token_type, bare_hash, first_arg);
|
2346
2389
|
if (current_token().type() == Token::Type::StarStar)
|
2347
2390
|
hash.static_cast_as<HashNode>()->add_node(parse_keyword_splat(locals));
|
2348
2391
|
return hash;
|
@@ -2699,6 +2742,9 @@ SharedPtr<Node> Parser::parse_unless(LocalsHashmap &locals) {
|
|
2699
2742
|
auto token = current_token();
|
2700
2743
|
advance();
|
2701
2744
|
SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals);
|
2745
|
+
if (condition->type() == Node::Type::Regexp) {
|
2746
|
+
condition = new MatchNode { condition->token(), condition.static_cast_as<RegexpNode>() };
|
2747
|
+
}
|
2702
2748
|
next_expression();
|
2703
2749
|
SharedPtr<Node> false_expr = parse_if_body(locals);
|
2704
2750
|
SharedPtr<Node> true_expr;
|
@@ -2717,6 +2763,9 @@ SharedPtr<Node> Parser::parse_while(LocalsHashmap &locals) {
|
|
2717
2763
|
auto token = current_token();
|
2718
2764
|
advance();
|
2719
2765
|
SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals);
|
2766
|
+
if (condition->type() == Node::Type::Regexp) {
|
2767
|
+
condition = new MatchNode { condition->token(), condition.static_cast_as<RegexpNode>() };
|
2768
|
+
}
|
2720
2769
|
next_expression();
|
2721
2770
|
SharedPtr<BlockNode> body = parse_body(locals, Precedence::LOWEST);
|
2722
2771
|
expect(Token::Type::EndKeyword, "while end");
|
@@ -2769,6 +2818,8 @@ Parser::parse_null_fn Parser::null_denotation(Token::Type type) {
|
|
2769
2818
|
return &Parser::parse_end_block;
|
2770
2819
|
case Type::FILEKeyword:
|
2771
2820
|
return &Parser::parse_file_constant;
|
2821
|
+
case Type::ForKeyword:
|
2822
|
+
return &Parser::parse_for;
|
2772
2823
|
case Type::LParen:
|
2773
2824
|
return &Parser::parse_group;
|
2774
2825
|
case Type::LCurlyBrace:
|
@@ -2790,7 +2841,7 @@ Parser::parse_null_fn Parser::null_denotation(Token::Type type) {
|
|
2790
2841
|
case Type::InterpolatedSymbolBegin:
|
2791
2842
|
return &Parser::parse_interpolated_symbol;
|
2792
2843
|
case Type::StarStar:
|
2793
|
-
return &Parser::
|
2844
|
+
return &Parser::parse_keyword_splat;
|
2794
2845
|
case Type::Bignum:
|
2795
2846
|
case Type::Fixnum:
|
2796
2847
|
case Type::Float:
|
@@ -2886,7 +2937,7 @@ Parser::parse_left_fn Parser::left_denotation(Token &token, SharedPtr<Node> left
|
|
2886
2937
|
return &Parser::parse_infix_expression;
|
2887
2938
|
case Type::Minus:
|
2888
2939
|
case Type::Plus:
|
2889
|
-
if (peek_token().whitespace_precedes() || !left->is_callable())
|
2940
|
+
if (!token.whitespace_precedes() || peek_token().whitespace_precedes() || !left->is_callable())
|
2890
2941
|
return &Parser::parse_infix_expression;
|
2891
2942
|
break;
|
2892
2943
|
case Type::DoKeyword:
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: natalie_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Morgan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-08-12 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: NatalieParser is a zero-dependency, from-scratch, hand-written recursive
|
14
14
|
descent parser for the Ruby Programming Language.
|
@@ -65,6 +65,7 @@ files:
|
|
65
65
|
- include/natalie_parser/node/false_node.hpp
|
66
66
|
- include/natalie_parser/node/fixnum_node.hpp
|
67
67
|
- include/natalie_parser/node/float_node.hpp
|
68
|
+
- include/natalie_parser/node/for_node.hpp
|
68
69
|
- include/natalie_parser/node/forward_args_node.hpp
|
69
70
|
- include/natalie_parser/node/hash_node.hpp
|
70
71
|
- include/natalie_parser/node/hash_pattern_node.hpp
|