natalie_parser 2.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/natalie_parser.svg)](https://badge.fury.io/rb/natalie_parser)
|
3
4
|
[![github build status](https://github.com/natalie-lang/natalie_parser/actions/workflows/build.yml/badge.svg)](https://github.com/natalie-lang/natalie_parser/actions?query=workflow%3ABuild+branch%3Amaster)
|
4
5
|
[![MIT License](https://img.shields.io/badge/license-MIT-blue)](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
|