natalie_parser 2.2.0 → 2.3.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 +12 -0
- data/Dockerfile +1 -1
- data/Gemfile +1 -1
- data/README.md +1 -0
- data/Rakefile +2 -1
- data/ext/natalie_parser/mri_creator.hpp +5 -5
- data/ext/natalie_parser/natalie_parser.cpp +4 -4
- data/include/natalie_parser/node/begin_rescue_node.hpp +4 -5
- data/include/natalie_parser/node/for_node.hpp +1 -16
- data/include/natalie_parser/parser.hpp +1 -0
- data/lib/natalie_parser/version.rb +1 -1
- data/src/lexer.cpp +24 -1
- data/src/node/begin_rescue_node.cpp +4 -3
- data/src/node/for_node.cpp +13 -0
- data/src/parser.cpp +22 -25
- 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: 2de813d6f0cb6ab94d5a6bb2dd64dc8be719567d401f19ea2b6ac9e71ebaed65
|
4
|
+
data.tar.gz: 668f9574d0968781f077e407f6476f2b320195964f8c10342ab2761beb3f7587
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02da9b2afc4bc76c5b76b03abb48a40bb9242e2884146bedd61b7b944f358401655aeb077bf7900eaf6c3936c3d06000115d20200b378d17bf98cc18ffd28174
|
7
|
+
data.tar.gz: 5e42458d1ab43bce763d3b929b095f9df5ab620e0c9ba25caac5abb8bd44673ded90bcbcd943b94ffe6cadf65d28f5bfebb54bb50942909c4055feb5c613cb2f
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 2.3.0 (2022-12-31)
|
4
|
+
|
5
|
+
- FIX: Add for loop variables to local scope
|
6
|
+
- FIX: Add lexer support for leading-zero (bare) octal - e.g. 0777
|
7
|
+
- FIX: Allow non-local variable in rescue
|
8
|
+
- FIX: Allow trailing comma after for loop variables
|
9
|
+
- FIX: Fix for-loop with attr assignment
|
10
|
+
- FIX: Fix lexing of symbol keys with trailing punctuation
|
11
|
+
- FIX: Fix precedence of 'do' keyword in use with 'for'
|
12
|
+
- FIX: Parse for loop with splat assignment
|
13
|
+
- FIX: Use proper make on OpenBSD
|
14
|
+
|
3
15
|
## 2.2.0 (2022-10-24)
|
4
16
|
|
5
17
|
- FIX: Allow backreference as first arg of implicit call
|
data/Dockerfile
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# Natalie Parser
|
2
2
|
|
3
|
+
[](https://badge.fury.io/rb/natalie_parser)
|
3
4
|
[](https://github.com/natalie-lang/natalie_parser/actions?query=workflow%3ABuild+branch%3Amaster)
|
4
5
|
[](https://github.com/natalie-lang/natalie_parser/blob/master/LICENSE)
|
5
6
|
|
data/Rakefile
CHANGED
@@ -185,7 +185,8 @@ file "ext/natalie_parser/natalie_parser.#{so_ext}" => [
|
|
185
185
|
else
|
186
186
|
cxx_hacky = cxx
|
187
187
|
end
|
188
|
-
|
188
|
+
make = system('which gmake 2>&1 > /dev/null') ? 'gmake' : 'make'
|
189
|
+
sh "CC=#{cc.inspect} CXX=#{cxx_hacky.inspect} #{make} -C #{build_dir} -j -e V=1 2>&1 | tee #{log_file}"
|
189
190
|
end
|
190
191
|
|
191
192
|
file 'build/fragments.hpp' => ['test/parser_test.rb', 'test/support/extract_parser_test_fragments.rb'] do
|
@@ -25,7 +25,7 @@ public:
|
|
25
25
|
|
26
26
|
virtual void reset_sexp() override {
|
27
27
|
m_sexp = rb_class_new_instance(0, nullptr, Sexp);
|
28
|
-
rb_ivar_set(m_sexp, rb_intern("@file"), get_file_string(file()));
|
28
|
+
rb_ivar_set(m_sexp, rb_intern("@file"), get_file_string(*file()));
|
29
29
|
rb_ivar_set(m_sexp, rb_intern("@line"), rb_int_new(line() + 1));
|
30
30
|
rb_ivar_set(m_sexp, rb_intern("@column"), rb_int_new(column() + 1));
|
31
31
|
}
|
@@ -139,13 +139,13 @@ public:
|
|
139
139
|
private:
|
140
140
|
VALUE m_sexp { Qnil };
|
141
141
|
|
142
|
-
static VALUE get_file_string(
|
143
|
-
auto file_string = s_file_cache.get(
|
142
|
+
static VALUE get_file_string(const String &file) {
|
143
|
+
auto file_string = s_file_cache.get(file);
|
144
144
|
if (!file_string) {
|
145
|
-
file_string = rb_str_new(file
|
145
|
+
file_string = rb_str_new(file.c_str(), file.length());
|
146
146
|
// FIXME: Seems there is no way to un-register and object. :-(
|
147
147
|
rb_gc_register_mark_object(file_string);
|
148
|
-
s_file_cache.put(
|
148
|
+
s_file_cache.put(file, file_string);
|
149
149
|
}
|
150
150
|
return file_string;
|
151
151
|
}
|
@@ -27,9 +27,9 @@ VALUE initialize(int argc, VALUE *argv, VALUE self) {
|
|
27
27
|
return self;
|
28
28
|
}
|
29
29
|
|
30
|
-
VALUE node_to_ruby(
|
31
|
-
NatalieParser::MRICreator creator { node
|
32
|
-
node
|
30
|
+
VALUE node_to_ruby(const NatalieParser::Node &node) {
|
31
|
+
NatalieParser::MRICreator creator { node };
|
32
|
+
node.transform(&creator);
|
33
33
|
return creator.sexp();
|
34
34
|
}
|
35
35
|
|
@@ -41,7 +41,7 @@ VALUE parse_on_instance(VALUE self) {
|
|
41
41
|
auto parser = NatalieParser::Parser { code_string, path_string };
|
42
42
|
try {
|
43
43
|
auto tree = parser.tree();
|
44
|
-
VALUE ast = node_to_ruby(tree);
|
44
|
+
VALUE ast = node_to_ruby(*tree);
|
45
45
|
return ast;
|
46
46
|
} catch (NatalieParser::Parser::SyntaxError &error) {
|
47
47
|
rb_raise(rb_eSyntaxError, "%s", error.message());
|
@@ -1,7 +1,6 @@
|
|
1
1
|
#pragma once
|
2
2
|
|
3
3
|
#include "natalie_parser/node/block_node.hpp"
|
4
|
-
#include "natalie_parser/node/identifier_node.hpp"
|
5
4
|
#include "natalie_parser/node/node.hpp"
|
6
5
|
#include "natalie_parser/node/node_with_args.hpp"
|
7
6
|
#include "tm/hashmap.hpp"
|
@@ -23,24 +22,24 @@ public:
|
|
23
22
|
m_exceptions.push(node);
|
24
23
|
}
|
25
24
|
|
26
|
-
void set_exception_name(SharedPtr<
|
25
|
+
void set_exception_name(SharedPtr<Node> name) {
|
27
26
|
m_name = name;
|
28
27
|
}
|
29
28
|
|
30
29
|
void set_body(SharedPtr<BlockNode> body) { m_body = body; }
|
31
30
|
|
32
|
-
SharedPtr<Node>
|
31
|
+
SharedPtr<Node> name_to_assignment() const;
|
33
32
|
|
34
33
|
bool has_name() const { return m_name; }
|
35
34
|
|
36
|
-
const SharedPtr<
|
35
|
+
const SharedPtr<Node> name() const { return m_name; }
|
37
36
|
const Vector<SharedPtr<Node>> &exceptions() const { return m_exceptions; }
|
38
37
|
const SharedPtr<BlockNode> body() const { return m_body; }
|
39
38
|
|
40
39
|
virtual void transform(Creator *creator) const override;
|
41
40
|
|
42
41
|
protected:
|
43
|
-
SharedPtr<
|
42
|
+
SharedPtr<Node> m_name {};
|
44
43
|
Vector<SharedPtr<Node>> m_exceptions {};
|
45
44
|
SharedPtr<BlockNode> m_body {};
|
46
45
|
};
|
@@ -25,22 +25,7 @@ public:
|
|
25
25
|
const SharedPtr<Node> vars() const { return m_vars; }
|
26
26
|
const SharedPtr<BlockNode> body() const { return m_body; }
|
27
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
|
-
}
|
28
|
+
virtual void transform(Creator *creator) const override;
|
44
29
|
|
45
30
|
protected:
|
46
31
|
SharedPtr<Node> m_expr {};
|
@@ -161,6 +161,7 @@ private:
|
|
161
161
|
SharedPtr<Node> parse_assignment_expression(SharedPtr<Node>, LocalsHashmap &);
|
162
162
|
SharedPtr<Node> parse_assignment_expression_without_multiple_values(SharedPtr<Node>, LocalsHashmap &);
|
163
163
|
SharedPtr<Node> parse_assignment_expression(SharedPtr<Node>, LocalsHashmap &, bool);
|
164
|
+
void add_assignment_locals(SharedPtr<Node>, LocalsHashmap &);
|
164
165
|
SharedPtr<Node> parse_assignment_expression_value(bool, LocalsHashmap &, bool);
|
165
166
|
SharedPtr<Node> parse_assignment_identifier(bool, LocalsHashmap &);
|
166
167
|
SharedPtr<Node> parse_call_expression_without_parens(SharedPtr<Node>, LocalsHashmap &);
|
data/src/lexer.cpp
CHANGED
@@ -975,6 +975,10 @@ Token Lexer::consume_bare_name_or_constant(Token::Type type) {
|
|
975
975
|
switch (c) {
|
976
976
|
case '?':
|
977
977
|
case '!':
|
978
|
+
if (peek() == ':' && m_last_token.can_precede_symbol_key()) {
|
979
|
+
advance();
|
980
|
+
type = Token::Type::SymbolKey;
|
981
|
+
}
|
978
982
|
advance();
|
979
983
|
buf->append_char(c);
|
980
984
|
break;
|
@@ -1284,7 +1288,26 @@ Token Lexer::consume_numeric() {
|
|
1284
1288
|
break;
|
1285
1289
|
}
|
1286
1290
|
default:
|
1287
|
-
|
1291
|
+
char c = peek();
|
1292
|
+
|
1293
|
+
if (isdigit(c)) {
|
1294
|
+
// bare octal case, e.g. 0777.
|
1295
|
+
// If starts with a 0 but next number is not 0..7 then that's an error.
|
1296
|
+
if (!(c >= '0' && c <= '7'))
|
1297
|
+
return Token { Token::Type::Invalid, c, m_file, m_cursor_line,
|
1298
|
+
m_cursor_column, m_whitespace_precedes };
|
1299
|
+
chars->append_char(c);
|
1300
|
+
c = next();
|
1301
|
+
do {
|
1302
|
+
chars->append_char(c);
|
1303
|
+
c = next();
|
1304
|
+
if (c == '_')
|
1305
|
+
c = next();
|
1306
|
+
} while (c >= '0' && c <= '7');
|
1307
|
+
token = chars_to_fixnum_or_bignum_token(chars, 8, 1);
|
1308
|
+
} else {
|
1309
|
+
token = consume_decimal_digits_and_build_token();
|
1310
|
+
}
|
1288
1311
|
}
|
1289
1312
|
} else {
|
1290
1313
|
token = consume_decimal_digits_and_build_token();
|
@@ -1,14 +1,15 @@
|
|
1
1
|
#include "natalie_parser/node/begin_rescue_node.hpp"
|
2
2
|
#include "natalie_parser/node/array_node.hpp"
|
3
3
|
#include "natalie_parser/node/assignment_node.hpp"
|
4
|
+
#include "natalie_parser/node/identifier_node.hpp"
|
4
5
|
|
5
6
|
namespace NatalieParser {
|
6
7
|
|
7
|
-
SharedPtr<Node> BeginRescueNode::
|
8
|
+
SharedPtr<Node> BeginRescueNode::name_to_assignment() const {
|
8
9
|
assert(m_name);
|
9
10
|
return new AssignmentNode {
|
10
11
|
token(),
|
11
|
-
m_name
|
12
|
+
m_name,
|
12
13
|
new IdentifierNode {
|
13
14
|
Token { Token::Type::GlobalVariable, "$!", file(), line(), column(), false },
|
14
15
|
false },
|
@@ -21,7 +22,7 @@ void BeginRescueNode::transform(Creator *creator) const {
|
|
21
22
|
for (auto exception_node : m_exceptions)
|
22
23
|
array.add_node(exception_node);
|
23
24
|
if (m_name)
|
24
|
-
array.add_node(
|
25
|
+
array.add_node(name_to_assignment());
|
25
26
|
creator->append(array);
|
26
27
|
if (m_body->nodes().is_empty())
|
27
28
|
creator->append_nil();
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#include "natalie_parser/node/for_node.hpp"
|
2
|
+
|
3
|
+
namespace NatalieParser {
|
4
|
+
|
5
|
+
void ForNode::transform(Creator *creator) const {
|
6
|
+
creator->set_type("for");
|
7
|
+
creator->append(m_expr);
|
8
|
+
creator->with_assignment(true, [&]() { creator->append(*m_vars); });
|
9
|
+
if (!m_body->is_empty())
|
10
|
+
creator->append(m_body->without_unnecessary_nesting());
|
11
|
+
}
|
12
|
+
|
13
|
+
}
|
data/src/parser.cpp
CHANGED
@@ -434,16 +434,8 @@ void Parser::parse_rest_of_begin(BeginNode &begin_node, LocalsHashmap &locals) {
|
|
434
434
|
}
|
435
435
|
if (current_token().is_hash_rocket()) {
|
436
436
|
advance();
|
437
|
-
|
438
|
-
|
439
|
-
case Token::Type::BareName:
|
440
|
-
name = new IdentifierNode { current_token(), current_token().literal_string() };
|
441
|
-
advance();
|
442
|
-
break;
|
443
|
-
default:
|
444
|
-
throw_unexpected("exception name");
|
445
|
-
}
|
446
|
-
name->add_to_locals(locals);
|
437
|
+
auto name = parse_expression(Precedence::ASSIGNMENT_LHS, locals);
|
438
|
+
add_assignment_locals(name, locals);
|
447
439
|
rescue_node->set_exception_name(name);
|
448
440
|
}
|
449
441
|
next_expression();
|
@@ -910,7 +902,7 @@ SharedPtr<Node> Parser::parse_multiple_assignment_expression(SharedPtr<Node> lef
|
|
910
902
|
list->add_node(left);
|
911
903
|
while (current_token().is_comma()) {
|
912
904
|
advance();
|
913
|
-
if (current_token().is_rparen() || current_token().is_equal()) {
|
905
|
+
if (current_token().is_rparen() || current_token().is_equal() || current_token().type() == Token::Type::InKeyword) {
|
914
906
|
// trailing comma with no additional identifier
|
915
907
|
break;
|
916
908
|
}
|
@@ -1296,13 +1288,16 @@ SharedPtr<Node> Parser::parse_line_constant(LocalsHashmap &) {
|
|
1296
1288
|
SharedPtr<Node> Parser::parse_for(LocalsHashmap &locals) {
|
1297
1289
|
auto token = current_token();
|
1298
1290
|
advance();
|
1299
|
-
auto vars = parse_assignment_identifier(
|
1300
|
-
if (current_token().type() ==
|
1291
|
+
auto vars = parse_assignment_identifier(true, locals);
|
1292
|
+
if (current_token().is_comma() || vars->type() == Node::Type::Splat) {
|
1301
1293
|
vars = parse_multiple_assignment_expression(vars, locals);
|
1302
1294
|
}
|
1295
|
+
add_assignment_locals(vars, locals);
|
1303
1296
|
expect(Token::Type::InKeyword, "for in");
|
1304
1297
|
advance();
|
1305
|
-
|
1298
|
+
m_call_depth.last()++;
|
1299
|
+
auto expr = parse_expression(Precedence::BARE_CALL_ARG, locals, IterAllow::CURLY_ONLY);
|
1300
|
+
m_call_depth.last()--;
|
1306
1301
|
if (current_token().type() == Token::Type::DoKeyword) {
|
1307
1302
|
advance();
|
1308
1303
|
}
|
@@ -2177,32 +2172,34 @@ SharedPtr<Node> Parser::parse_assignment_expression(SharedPtr<Node> left, Locals
|
|
2177
2172
|
if (left->type() == Node::Type::Splat) {
|
2178
2173
|
return parse_multiple_assignment_expression(left, locals);
|
2179
2174
|
}
|
2175
|
+
add_assignment_locals(left, locals);
|
2176
|
+
advance();
|
2177
|
+
bool to_array = left->type() == Node::Type::MultipleAssignment;
|
2178
|
+
auto value = parse_assignment_expression_value(to_array, locals, allow_multiple);
|
2179
|
+
return new AssignmentNode { token, left, value };
|
2180
|
+
}
|
2181
|
+
|
2182
|
+
void Parser::add_assignment_locals(SharedPtr<Node> left, LocalsHashmap &locals) {
|
2180
2183
|
switch (left->type()) {
|
2181
2184
|
case Node::Type::Identifier: {
|
2182
2185
|
auto left_identifier = left.static_cast_as<IdentifierNode>();
|
2183
2186
|
left_identifier->add_to_locals(locals);
|
2184
|
-
|
2185
|
-
auto value = parse_assignment_expression_value(false, locals, allow_multiple);
|
2186
|
-
return new AssignmentNode { token, left, value };
|
2187
|
+
break;
|
2187
2188
|
}
|
2188
2189
|
case Node::Type::Call:
|
2189
2190
|
case Node::Type::Colon2:
|
2190
2191
|
case Node::Type::Colon3:
|
2191
2192
|
case Node::Type::SafeCall: {
|
2192
|
-
|
2193
|
-
auto value = parse_assignment_expression_value(false, locals, allow_multiple);
|
2194
|
-
return new AssignmentNode { token, left, value };
|
2193
|
+
break;
|
2195
2194
|
}
|
2196
2195
|
case Node::Type::MultipleAssignment: {
|
2197
2196
|
left.static_cast_as<MultipleAssignmentNode>()->add_locals(locals);
|
2198
|
-
|
2199
|
-
auto value = parse_assignment_expression_value(true, locals, allow_multiple);
|
2200
|
-
return new AssignmentNode { token, left, value };
|
2197
|
+
break;
|
2201
2198
|
}
|
2202
2199
|
default:
|
2203
|
-
throw_unexpected(left->token(), "
|
2200
|
+
throw_unexpected(left->token(), "assignment identifier");
|
2204
2201
|
}
|
2205
|
-
}
|
2202
|
+
}
|
2206
2203
|
|
2207
2204
|
SharedPtr<Node> Parser::parse_assignment_expression_value(bool to_array, LocalsHashmap &locals, bool allow_multiple) {
|
2208
2205
|
auto token = current_token();
|
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: 2.
|
4
|
+
version: 2.3.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-12-31 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.
|
@@ -151,6 +151,7 @@ files:
|
|
151
151
|
- src/node/begin_node.cpp
|
152
152
|
- src/node/begin_rescue_node.cpp
|
153
153
|
- src/node/class_node.cpp
|
154
|
+
- src/node/for_node.cpp
|
154
155
|
- src/node/interpolated_regexp_node.cpp
|
155
156
|
- src/node/interpolated_shell_node.cpp
|
156
157
|
- src/node/interpolated_string_node.cpp
|