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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 31bee2746c1e7a36eca16194e27467264dfbad5131075090e0b94c675ff150b1
4
- data.tar.gz: 7f9a2a77aa2e34c56faeb740900779ff4682471cb02dbd51403eb1b7f72b13a7
3
+ metadata.gz: 2de813d6f0cb6ab94d5a6bb2dd64dc8be719567d401f19ea2b6ac9e71ebaed65
4
+ data.tar.gz: 668f9574d0968781f077e407f6476f2b320195964f8c10342ab2761beb3f7587
5
5
  SHA512:
6
- metadata.gz: 74335871743111340267d41631f3eeae0281ea5f93bb2bb4a6536682a55f325162d428d986ba0182e14adfb27e830f9622048fb443f1561ee9611c988b07bd6b
7
- data.tar.gz: 2fd301c60b32db6c074946adceaa54fca3736f6e64335d5bb68c9ad01f092bf19f1ce2810fb75e4641e4b6cf7ebf7e77165538282107f214fa116254b0d37ac0
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
@@ -8,7 +8,7 @@ ENV LC_ALL C.UTF-8
8
8
 
9
9
  WORKDIR natalie_parser
10
10
 
11
- COPY Gemfile /natalie_parser/
11
+ COPY Gemfile /natalie_parser/
12
12
  RUN bundle install
13
13
 
14
14
  ARG CC=gcc
data/Gemfile CHANGED
@@ -6,5 +6,5 @@ source 'https://rubygems.org'
6
6
  gem 'minitest'
7
7
  gem 'minitest-focus'
8
8
  gem 'minitest-reporters'
9
- gem 'ruby_parser'
9
+ gem 'ruby_parser', '3.19.1'
10
10
  gem 'rake'
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
- sh "CC=#{cc.inspect} CXX=#{cxx_hacky.inspect} make -C #{build_dir} -j -e V=1 2>&1 | tee #{log_file}"
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(SharedPtr<const String> file) {
143
- auto file_string = s_file_cache.get(*file);
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->c_str(), file->length());
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(*file, file_string);
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(TM::SharedPtr<NatalieParser::Node> node) {
31
- NatalieParser::MRICreator creator { node.ref() };
32
- node->transform(&creator);
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<IdentifierNode> name) {
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> name_to_node() const;
31
+ SharedPtr<Node> name_to_assignment() const;
33
32
 
34
33
  bool has_name() const { return m_name; }
35
34
 
36
- const SharedPtr<IdentifierNode> name() const { return m_name; }
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<IdentifierNode> m_name {};
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 &);
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class NatalieParser
4
- VERSION = '2.2.0'
4
+ VERSION = '2.3.0'
5
5
  end
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
- token = consume_decimal_digits_and_build_token();
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::name_to_node() const {
8
+ SharedPtr<Node> BeginRescueNode::name_to_assignment() const {
8
9
  assert(m_name);
9
10
  return new AssignmentNode {
10
11
  token(),
11
- m_name.static_cast_as<Node>(),
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(name_to_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
- SharedPtr<IdentifierNode> name;
438
- switch (current_token().type()) {
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(false, locals);
1300
- if (current_token().type() == Token::Type::Comma) {
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
- auto expr = parse_expression(Precedence::LOWEST, locals, IterAllow::CURLY_ONLY);
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
- advance();
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
- advance();
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
- advance();
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(), "left side of assignment");
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.2.0
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-10-25 00:00:00.000000000 Z
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