natalie_parser 1.1.1 → 2.0.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: a06594f8ef75760689083616aefd09c426554ac59e07e37958102ef7bcf514b4
4
- data.tar.gz: 2bc38fd3e4dd891b1b5251ed9ba6853d9cc5aa1ca3afa8ffe1af614cb5aa318f
3
+ metadata.gz: b35f9b98f6bbe4ec3bf8d5d9bce2c7e082504db23f442964f2a58e1ac9e11dc7
4
+ data.tar.gz: 1fbf06aa6fae8400855ea0d363d77a1b27d78869266bfb17b0f45cf9b8a30d41
5
5
  SHA512:
6
- metadata.gz: 05515d488cd67f0bbe50c24724248b73cb026b2fc73580419fd1fc017146706ac877fe8a103b14005649695732ca10f293aba8b48d6aee0eb9cd72087e44d339
7
- data.tar.gz: 7d55f78cd2e539b4d28951fd50bc3f278df631c63c7aba82d1af6af6b6e085790ecdacc426747f097fb6047f13f178d66c4a751c882f99a115004e39ce45cd70
6
+ metadata.gz: 936ec0ef70541afd5839dbe4450401d7e8713499c3536d9ac7e53eb5617671dd308eee8e4f458188a9b0890f5a7ed5d855b3011c5539b64eb31639b4140e07fc
7
+ data.tar.gz: f6deb35c75e17e5c9fb49c6092e48453e904e7c7d4b515e2c7b5fe7fe69dc656da2faa6096b665b6021557ae8c47844ec65a9e8aca1933712f35d72f4db47c50
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.0 (2022-06-24)
4
+
5
+ - FEAT: Differentiate between bare/implicit hash and explicit one
6
+ - FIX: Fix calling colon2
7
+ - FIX: Parse implicit method calls with nth ref argument
8
+
9
+ ## 1.2.1 (2022-06-16)
10
+
11
+ - FIX: Fix regression with unary/infix operators (+/-)
12
+
13
+ ## 1.2.0 (2022-06-16)
14
+
15
+ - CHORE: Enable true random fuzzing
16
+ - FEAT: Add Node::debug() function to help with debugging
17
+ - FEAT: Parse more pattern matching cases
18
+ - FIX: Don't error if ext/natalie_parser/build.log isn't written yet
19
+ - FIX: Fix block association inside call with parentheses
20
+ - FIX: Fix bug negating an already-negative number
21
+ - FIX: Fix bug parsing unary operator as an infix operation
22
+ - FIX: Fix method definition with lonely ensure
23
+ - FIX: Fix parsing endless ranges inside case/when and other odd places
24
+ - FIX: Make sure every token knows if it has preceding whitespace
25
+ - FIX: Parse method calls with constant receiver
26
+
3
27
  ## 1.1.1 (2022-06-04)
4
28
 
5
29
  - FIX: Workaround for clang declspec bug
@@ -8,7 +32,7 @@
8
32
 
9
33
  - CHORE: Add ccache and compiledb for the ext/natalie_parser directory
10
34
  - CHORE: Add tests for numbered block arg shorthand
11
- - FEAT Parse arg forwarding (...) shorthand
35
+ - FEAT: Parse arg forwarding (...) shorthand
12
36
  - FEAT: Parse complex and rational numbers
13
37
  - FIX: Fix panic when closing word array delimiter is not found
14
38
  - FIX: Fix precedence bug with op assign operators (+= et al)
data/Rakefile CHANGED
@@ -127,7 +127,9 @@ if system('which compiledb 2>&1 >/dev/null')
127
127
  if $compiledb_out.any?
128
128
  File.write('build/build.log', $compiledb_out.join("\n"))
129
129
  sh 'compiledb < build/build.log'
130
- sh 'cd ext/natalie_parser && compiledb < build.log'
130
+ if File.exist?('ext/natalie_parser/build.log')
131
+ sh 'cd ext/natalie_parser && compiledb < build.log'
132
+ end
131
133
  end
132
134
  end
133
135
  else
@@ -3,6 +3,8 @@
3
3
  #include "natalie_parser/node/array_node.hpp"
4
4
  #include "natalie_parser/node/node.hpp"
5
5
  #include "natalie_parser/node/node_with_args.hpp"
6
+ #include "natalie_parser/node/splat_node.hpp"
7
+ #include "natalie_parser/node/symbol_node.hpp"
6
8
  #include "tm/hashmap.hpp"
7
9
  #include "tm/string.hpp"
8
10
 
@@ -15,14 +17,30 @@ public:
15
17
  ArrayPatternNode(const Token &token)
16
18
  : ArrayNode { token } { }
17
19
 
20
+ ArrayPatternNode(const Token &token, SharedPtr<Node> node)
21
+ : ArrayNode { token } {
22
+ m_nodes.push(node);
23
+ }
24
+
18
25
  virtual Type type() const override { return Type::ArrayPattern; }
19
26
 
20
27
  virtual void transform(Creator *creator) const override {
21
28
  creator->set_type("array_pat");
22
29
  if (!m_nodes.is_empty())
23
30
  creator->append_nil(); // NOTE: I don't know what this nil is for
24
- for (auto node : m_nodes)
25
- creator->append(node);
31
+ for (auto node : m_nodes) {
32
+ if (node->type() == Node::Type::Splat) {
33
+ auto splat_node = node.static_cast_as<SplatNode>();
34
+ auto name = String("*");
35
+ if (splat_node->node()) {
36
+ assert(splat_node->node()->type() == Node::Type::Symbol);
37
+ name.append(splat_node->node().static_cast_as<SymbolNode>()->name().ref());
38
+ }
39
+ creator->append_symbol(name);
40
+ } else {
41
+ creator->append(node);
42
+ }
43
+ }
26
44
  }
27
45
  };
28
46
  }
@@ -31,6 +31,10 @@ public:
31
31
  m_number->prepend_char('-');
32
32
  }
33
33
 
34
+ bool negative() const {
35
+ return m_number->at(0) == '-';
36
+ }
37
+
34
38
  protected:
35
39
  SharedPtr<String> m_number;
36
40
  };
@@ -29,8 +29,11 @@ public:
29
29
  virtual void transform(Creator *creator) const override {
30
30
  creator->set_type("in");
31
31
  creator->append(m_pattern.ref());
32
- for (auto node : m_body->nodes())
33
- creator->append(node);
32
+ if (!m_body->is_empty())
33
+ for (auto node : m_body->nodes())
34
+ creator->append(node);
35
+ else
36
+ creator->append_nil();
34
37
  }
35
38
 
36
39
  protected:
@@ -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; }
@@ -30,6 +30,10 @@ public:
30
30
  m_number *= -1;
31
31
  }
32
32
 
33
+ bool negative() const {
34
+ return m_number < 0;
35
+ }
36
+
33
37
  protected:
34
38
  long long m_number;
35
39
  };
@@ -30,6 +30,10 @@ public:
30
30
  m_number *= -1;
31
31
  }
32
32
 
33
+ bool negative() const {
34
+ return m_number < 0.0;
35
+ }
36
+
33
37
  protected:
34
38
  double m_number;
35
39
  };
@@ -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
- creator->set_type("hash");
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
  }
@@ -3,6 +3,7 @@
3
3
  #include "natalie_parser/node/hash_node.hpp"
4
4
  #include "natalie_parser/node/node.hpp"
5
5
  #include "natalie_parser/node/node_with_args.hpp"
6
+ #include "natalie_parser/node/symbol_key_node.hpp"
6
7
  #include "tm/hashmap.hpp"
7
8
  #include "tm/string.hpp"
8
9
 
@@ -13,7 +14,7 @@ using namespace TM;
13
14
  class HashPatternNode : public HashNode {
14
15
  public:
15
16
  HashPatternNode(const Token &token)
16
- : HashNode { token } { }
17
+ : HashNode { token, true } { }
17
18
 
18
19
  virtual Type type() const override { return Type::HashPattern; }
19
20
 
@@ -22,7 +22,7 @@ public:
22
22
 
23
23
  virtual Type type() const override { return Type::InfixOp; }
24
24
 
25
- virtual bool is_callable() const override { return true; }
25
+ virtual bool is_callable() const override { return false; }
26
26
  virtual bool can_accept_a_block() const override { return false; }
27
27
 
28
28
  const SharedPtr<Node> left() const { return m_left; }
@@ -0,0 +1,43 @@
1
+ #pragma once
2
+
3
+ #include "natalie_parser/node/array_node.hpp"
4
+ #include "natalie_parser/node/node.hpp"
5
+ #include "natalie_parser/node/node_with_args.hpp"
6
+ #include "natalie_parser/node/splat_node.hpp"
7
+ #include "natalie_parser/node/symbol_node.hpp"
8
+ #include "tm/hashmap.hpp"
9
+ #include "tm/string.hpp"
10
+
11
+ namespace NatalieParser {
12
+
13
+ using namespace TM;
14
+
15
+ class KeywordRestPatternNode : public Node {
16
+ public:
17
+ KeywordRestPatternNode(const Token &token)
18
+ : Node { token } { }
19
+
20
+ KeywordRestPatternNode(const Token &token, String name)
21
+ : Node { token }
22
+ , m_name { new String(name) } { }
23
+
24
+ KeywordRestPatternNode(const Token &token, SharedPtr<String> name)
25
+ : Node { token }
26
+ , m_name { name } { }
27
+
28
+ virtual Type type() const override { return Type::KeywordRestPattern; }
29
+
30
+ const SharedPtr<String> name() const { return m_name; }
31
+
32
+ virtual void transform(Creator *creator) const override {
33
+ creator->set_type("kwrest");
34
+ auto name = String("**");
35
+ if (m_name)
36
+ name.append(m_name.ref());
37
+ creator->append_symbol(name);
38
+ }
39
+
40
+ private:
41
+ SharedPtr<String> m_name;
42
+ };
43
+ }
@@ -56,6 +56,7 @@ public:
56
56
  InterpolatedSymbol,
57
57
  InterpolatedSymbolKey,
58
58
  KeywordArg,
59
+ KeywordRestPattern,
59
60
  KeywordSplat,
60
61
  LogicalAnd,
61
62
  LogicalOr,
@@ -150,6 +151,8 @@ public:
150
151
  return type() != Type::Invalid;
151
152
  }
152
153
 
154
+ void debug();
155
+
153
156
  protected:
154
157
  static inline SharedPtr<Node> s_invalid {};
155
158
  Token m_token {};
@@ -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 true; }
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; }
@@ -45,6 +45,7 @@
45
45
  #include "natalie_parser/node/interpolated_symbol_node.hpp"
46
46
  #include "natalie_parser/node/iter_node.hpp"
47
47
  #include "natalie_parser/node/keyword_arg_node.hpp"
48
+ #include "natalie_parser/node/keyword_rest_pattern_node.hpp"
48
49
  #include "natalie_parser/node/keyword_splat_node.hpp"
49
50
  #include "natalie_parser/node/logical_and_node.hpp"
50
51
  #include "natalie_parser/node/logical_or_node.hpp"
@@ -82,6 +82,8 @@ private:
82
82
  SharedPtr<Node> parse_class_or_module_name(LocalsHashmap &);
83
83
  SharedPtr<Node> parse_case(LocalsHashmap &);
84
84
  SharedPtr<Node> parse_case_in_pattern(LocalsHashmap &);
85
+ SharedPtr<Node> parse_case_in_pattern_alternation(LocalsHashmap &);
86
+ SharedPtr<Node> parse_case_in_pattern_hash_symbol_key(LocalsHashmap &);
85
87
  SharedPtr<Node> parse_case_in_patterns(LocalsHashmap &);
86
88
  void parse_comma_separated_expressions(ArrayNode &, LocalsHashmap &);
87
89
  SharedPtr<Node> parse_constant(LocalsHashmap &);
@@ -102,7 +104,7 @@ private:
102
104
  SharedPtr<Node> parse_forward_args(LocalsHashmap &);
103
105
  SharedPtr<Node> parse_group(LocalsHashmap &);
104
106
  SharedPtr<Node> parse_hash(LocalsHashmap &);
105
- SharedPtr<Node> parse_hash_inner(LocalsHashmap &, Precedence, Token::Type, SharedPtr<Node> = {});
107
+ SharedPtr<Node> parse_hash_inner(LocalsHashmap &, Precedence, Token::Type, bool, SharedPtr<Node> = {});
106
108
  SharedPtr<Node> parse_identifier(LocalsHashmap &);
107
109
  SharedPtr<Node> parse_if(LocalsHashmap &);
108
110
  void parse_interpolated_body(LocalsHashmap &, InterpolatedNode &, Token::Type);
@@ -197,6 +199,7 @@ private:
197
199
 
198
200
  SharedPtr<NodeWithArgs> to_node_with_args(SharedPtr<Node> node);
199
201
 
202
+ Token &previous_token() const;
200
203
  Token &current_token() const;
201
204
  Token &peek_token() const;
202
205
 
@@ -158,58 +158,64 @@ public:
158
158
 
159
159
  Token() { }
160
160
 
161
- Token(Type type, SharedPtr<String> file, size_t line, size_t column)
161
+ Token(Type type, SharedPtr<String> file, size_t line, size_t column, bool whitespace_precedes)
162
162
  : m_type { type }
163
163
  , m_file { file }
164
164
  , m_line { line }
165
- , m_column { column } {
165
+ , m_column { column }
166
+ , m_whitespace_precedes { whitespace_precedes } {
166
167
  assert(file);
167
168
  }
168
169
 
169
- Token(Type type, const char *literal, SharedPtr<String> file, size_t line, size_t column)
170
+ Token(Type type, const char *literal, SharedPtr<String> file, size_t line, size_t column, bool whitespace_precedes)
170
171
  : m_type { type }
171
172
  , m_literal { new String(literal) }
172
173
  , m_file { file }
173
174
  , m_line { line }
174
- , m_column { column } {
175
+ , m_column { column }
176
+ , m_whitespace_precedes { whitespace_precedes } {
175
177
  assert(literal);
176
178
  assert(file);
177
179
  }
178
180
 
179
- Token(Type type, SharedPtr<String> literal, SharedPtr<String> file, size_t line, size_t column)
181
+ Token(Type type, SharedPtr<String> literal, SharedPtr<String> file, size_t line, size_t column, bool whitespace_precedes)
180
182
  : m_type { type }
181
183
  , m_literal { literal }
182
184
  , m_file { file }
183
185
  , m_line { line }
184
- , m_column { column } {
186
+ , m_column { column }
187
+ , m_whitespace_precedes { whitespace_precedes } {
185
188
  assert(literal);
186
189
  assert(file);
187
190
  }
188
191
 
189
- Token(Type type, char literal, SharedPtr<String> file, size_t line, size_t column)
192
+ Token(Type type, char literal, SharedPtr<String> file, size_t line, size_t column, bool whitespace_precedes)
190
193
  : m_type { type }
191
194
  , m_literal { new String(literal) }
192
195
  , m_file { file }
193
196
  , m_line { line }
194
- , m_column { column } {
197
+ , m_column { column }
198
+ , m_whitespace_precedes { whitespace_precedes } {
195
199
  assert(file);
196
200
  }
197
201
 
198
- Token(Type type, long long fixnum, SharedPtr<String> file, size_t line, size_t column)
202
+ Token(Type type, long long fixnum, SharedPtr<String> file, size_t line, size_t column, bool whitespace_precedes)
199
203
  : m_type { type }
200
204
  , m_fixnum { fixnum }
201
205
  , m_file { file }
202
206
  , m_line { line }
203
- , m_column { column } {
207
+ , m_column { column }
208
+ , m_whitespace_precedes { whitespace_precedes } {
204
209
  assert(file);
205
210
  }
206
211
 
207
- Token(Type type, double dbl, SharedPtr<String> file, size_t line, size_t column)
212
+ Token(Type type, double dbl, SharedPtr<String> file, size_t line, size_t column, bool whitespace_precedes)
208
213
  : m_type { type }
209
214
  , m_double { dbl }
210
215
  , m_file { file }
211
216
  , m_line { line }
212
- , m_column { column } {
217
+ , m_column { column }
218
+ , m_whitespace_precedes { whitespace_precedes } {
213
219
  assert(file);
214
220
  }
215
221
 
@@ -636,6 +642,7 @@ public:
636
642
  bool is_elsif_keyword() const { return m_type == Type::ElsifKeyword; }
637
643
  bool is_end_keyword() const { return m_type == Type::EndKeyword; }
638
644
  bool is_end_of_expression() const { return m_type == Type::EndKeyword || m_type == Type::RCurlyBrace || m_type == Type::Newline || m_type == Type::Semicolon || m_type == Type::Eof || is_expression_modifier(); }
645
+ bool is_ensure() const { return m_type == Type::EnsureKeyword; }
639
646
  bool is_eof() const { return m_type == Type::Eof; }
640
647
  bool is_end_of_line() const { return m_type == Type::Newline || m_type == Type::Semicolon; }
641
648
  bool is_equal() const { return m_type == Type::Equal; }
@@ -644,6 +651,7 @@ public:
644
651
  bool is_lparen() const { return m_type == Type::LParen; }
645
652
  bool is_newline() const { return m_type == Type::Newline; }
646
653
  bool is_rbracket() const { return m_type == Type::RBracket; }
654
+ bool is_rescue() const { return m_type == Type::RescueKeyword; }
647
655
  bool is_rparen() const { return m_type == Type::RParen; }
648
656
  bool is_semicolon() const { return m_type == Type::Semicolon; }
649
657
  bool is_splat() const { return m_type == Type::Star || m_type == Type::StarStar; }
@@ -662,6 +670,7 @@ public:
662
670
  case Token::Type::RParen:
663
671
  case Token::Type::SafeNavigation:
664
672
  case Token::Type::TernaryColon:
673
+ case Token::Type::ThenKeyword:
665
674
  return true;
666
675
  default:
667
676
  return false;
@@ -680,7 +689,6 @@ public:
680
689
  case Token::Type::Comparison:
681
690
  case Token::Type::ConstantResolution:
682
691
  case Token::Type::Dot:
683
- case Token::Type::DotDot:
684
692
  case Token::Type::Equal:
685
693
  case Token::Type::EqualEqual:
686
694
  case Token::Type::EqualEqualEqual:
@@ -763,13 +771,16 @@ public:
763
771
  case Token::Type::LBracketRBracket:
764
772
  case Token::Type::LINEKeyword:
765
773
  case Token::Type::LParen:
774
+ case Token::Type::Minus:
766
775
  case Token::Type::NilKeyword:
767
776
  case Token::Type::Not:
768
777
  case Token::Type::NotKeyword:
778
+ case Token::Type::NthRef:
769
779
  case Token::Type::PercentLowerI:
770
780
  case Token::Type::PercentLowerW:
771
781
  case Token::Type::PercentUpperI:
772
782
  case Token::Type::PercentUpperW:
783
+ case Token::Type::Plus:
773
784
  case Token::Type::SelfKeyword:
774
785
  case Token::Type::Star:
775
786
  case Token::Type::String:
@@ -824,6 +835,25 @@ public:
824
835
  }
825
836
  }
826
837
 
838
+ bool can_be_range_arg_token() const {
839
+ if (is_closing_token())
840
+ return false;
841
+ if (is_semicolon() || is_eof())
842
+ return false;
843
+ switch (m_type) {
844
+ case Type::ElseKeyword:
845
+ case Type::ElsifKeyword:
846
+ case Type::EndKeyword:
847
+ case Type::InKeyword:
848
+ case Type::ThenKeyword:
849
+ case Type::WhenKeyword:
850
+ // TODO: likely many more cases!
851
+ return false;
852
+ default:
853
+ return true;
854
+ }
855
+ }
856
+
827
857
  void set_literal(const char *literal) { m_literal = new String(literal); }
828
858
  void set_literal(SharedPtr<String> literal) { m_literal = literal; }
829
859
  void set_literal(String literal) { m_literal = new String(literal); }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class NatalieParser
4
- VERSION = '1.1.1'
4
+ VERSION = '2.0.0'
5
5
  end
@@ -14,7 +14,7 @@ Token InterpolatedStringLexer::build_next_token() {
14
14
  case State::EndToken:
15
15
  return finish();
16
16
  case State::Done:
17
- return Token { Token::Type::Eof, m_file, m_cursor_line, m_cursor_column };
17
+ return Token { Token::Type::Eof, m_file, m_cursor_line, m_cursor_column, m_whitespace_precedes };
18
18
  }
19
19
  TM_UNREACHABLE();
20
20
  }
@@ -26,13 +26,13 @@ Token InterpolatedStringLexer::consume_string() {
26
26
  advance(); // backslash
27
27
  auto result = consume_escaped_byte(*buf);
28
28
  if (!result.first)
29
- return Token { result.second, current_char(), m_file, m_cursor_line, m_cursor_column };
29
+ return Token { result.second, current_char(), m_file, m_cursor_line, m_cursor_column, m_whitespace_precedes };
30
30
  } else if (c == '#' && peek() == '{') {
31
31
  if (buf->is_empty()) {
32
32
  advance(2);
33
33
  return start_evaluation();
34
34
  }
35
- auto token = Token { Token::Type::String, buf, m_file, m_token_line, m_token_column };
35
+ auto token = Token { Token::Type::String, buf, m_file, m_token_line, m_token_column, m_whitespace_precedes };
36
36
  advance(2);
37
37
  m_state = State::EvaluateBegin;
38
38
  return token;
@@ -49,7 +49,7 @@ Token InterpolatedStringLexer::consume_string() {
49
49
  return finish();
50
50
  } else {
51
51
  m_state = State::EndToken;
52
- return Token { Token::Type::String, buf, m_file, m_token_line, m_token_column };
52
+ return Token { Token::Type::String, buf, m_file, m_token_line, m_token_column, m_whitespace_precedes };
53
53
  }
54
54
  } else {
55
55
  buf->append_char(c);
@@ -62,27 +62,27 @@ Token InterpolatedStringLexer::consume_string() {
62
62
  if (m_stop_char == 0) {
63
63
  advance();
64
64
  m_state = State::EndToken;
65
- return Token { Token::Type::String, buf, m_file, m_token_line, m_token_column };
65
+ return Token { Token::Type::String, buf, m_file, m_token_line, m_token_column, m_whitespace_precedes };
66
66
  }
67
67
 
68
- return Token { Token::Type::UnterminatedString, buf, m_file, m_token_line, m_token_column };
68
+ return Token { Token::Type::UnterminatedString, buf, m_file, m_token_line, m_token_column, m_whitespace_precedes };
69
69
  }
70
70
 
71
71
  Token InterpolatedStringLexer::start_evaluation() {
72
72
  m_nested_lexer = new Lexer { *this, '{', '}' };
73
73
  m_state = State::EvaluateEnd;
74
- return Token { Token::Type::EvaluateToStringBegin, m_file, m_token_line, m_token_column };
74
+ return Token { Token::Type::EvaluateToStringBegin, m_file, m_token_line, m_token_column, m_whitespace_precedes };
75
75
  }
76
76
 
77
77
  Token InterpolatedStringLexer::stop_evaluation() {
78
78
  advance(); // }
79
79
  m_state = State::InProgress;
80
- return Token { Token::Type::EvaluateToStringEnd, m_file, m_token_line, m_token_column };
80
+ return Token { Token::Type::EvaluateToStringEnd, m_file, m_token_line, m_token_column, m_whitespace_precedes };
81
81
  }
82
82
 
83
83
  Token InterpolatedStringLexer::finish() {
84
84
  m_state = State::Done;
85
- return Token { m_end_type, m_file, m_cursor_line, m_cursor_column };
85
+ return Token { m_end_type, m_file, m_cursor_line, m_cursor_column, m_whitespace_precedes };
86
86
  }
87
87
 
88
88
  };
@@ -11,7 +11,7 @@ Token RegexpLexer::build_next_token() {
11
11
  m_nested_lexer = new Lexer { *this };
12
12
  m_nested_lexer->set_stop_char('}');
13
13
  m_state = State::EvaluateEnd;
14
- return Token { Token::Type::EvaluateToStringBegin, m_file, m_token_line, m_token_column };
14
+ return Token { Token::Type::EvaluateToStringBegin, m_file, m_token_line, m_token_column, m_whitespace_precedes };
15
15
  case State::EvaluateEnd:
16
16
  advance(); // }
17
17
  if (current_char() == m_stop_char) {
@@ -21,16 +21,16 @@ Token RegexpLexer::build_next_token() {
21
21
  } else {
22
22
  m_state = State::InProgress;
23
23
  }
24
- return Token { Token::Type::EvaluateToStringEnd, m_file, m_token_line, m_token_column };
24
+ return Token { Token::Type::EvaluateToStringEnd, m_file, m_token_line, m_token_column, m_whitespace_precedes };
25
25
  case State::EndToken: {
26
26
  m_state = State::Done;
27
- auto token = Token { Token::Type::InterpolatedRegexpEnd, m_file, m_cursor_line, m_cursor_column };
27
+ auto token = Token { Token::Type::InterpolatedRegexpEnd, m_file, m_cursor_line, m_cursor_column, m_whitespace_precedes };
28
28
  if (m_options && !m_options->is_empty())
29
29
  token.set_literal(m_options);
30
30
  return token;
31
31
  }
32
32
  case State::Done:
33
- return Token { Token::Type::Eof, m_file, m_cursor_line, m_cursor_column };
33
+ return Token { Token::Type::Eof, m_file, m_cursor_line, m_cursor_column, m_whitespace_precedes };
34
34
  }
35
35
  TM_UNREACHABLE();
36
36
  }
@@ -55,7 +55,7 @@ Token RegexpLexer::consume_regexp() {
55
55
  }
56
56
  advance();
57
57
  } else if (c == '#' && peek() == '{') {
58
- auto token = Token { Token::Type::String, buf, m_file, m_token_line, m_token_column };
58
+ auto token = Token { Token::Type::String, buf, m_file, m_token_line, m_token_column, m_whitespace_precedes };
59
59
  buf = new String;
60
60
  advance(2);
61
61
  m_state = State::EvaluateBegin;
@@ -72,14 +72,14 @@ Token RegexpLexer::consume_regexp() {
72
72
  } else {
73
73
  m_options = consume_options();
74
74
  m_state = State::EndToken;
75
- return Token { Token::Type::String, buf, m_file, m_token_line, m_token_column };
75
+ return Token { Token::Type::String, buf, m_file, m_token_line, m_token_column, m_whitespace_precedes };
76
76
  }
77
77
  } else {
78
78
  buf->append_char(c);
79
79
  advance();
80
80
  }
81
81
  }
82
- return Token { Token::Type::UnterminatedRegexp, buf, m_file, m_token_line, m_token_column };
82
+ return Token { Token::Type::UnterminatedRegexp, buf, m_file, m_token_line, m_token_column, m_whitespace_precedes };
83
83
  }
84
84
 
85
85
  String *RegexpLexer::consume_options() {