natalie_parser 1.1.1 → 1.2.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: db5a804188bcf10ccfbadada61b82e266f227650c5a0b53929f719975d967999
4
+ data.tar.gz: b14cc0efffacc5219f7bcd5174116d0f302f7d56c4c69bfc13214b7b653adbb7
5
5
  SHA512:
6
- metadata.gz: 05515d488cd67f0bbe50c24724248b73cb026b2fc73580419fd1fc017146706ac877fe8a103b14005649695732ca10f293aba8b48d6aee0eb9cd72087e44d339
7
- data.tar.gz: 7d55f78cd2e539b4d28951fd50bc3f278df631c63c7aba82d1af6af6b6e085790ecdacc426747f097fb6047f13f178d66c4a751c882f99a115004e39ce45cd70
6
+ metadata.gz: fb144d0f89276c280a3e7500f765cf98df9cffd4cfa8f0d0ed8d4a6d3a0d98f21b6b27d9d32b6b053a89edc0c092921e7d1215a546b7322162bd5bab7c37cf25
7
+ data.tar.gz: b467baf8f5619ea58d6ce8fb2802d63509b51b020459de8b2c8a53ed7af8927b45f3191ddda1429456d875f892be0ffc79f426915395f968210b90a030aab8f6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.0 (2022-06-16)
4
+
5
+ - CHORE: Enable true random fuzzing
6
+ - FEAT: Add Node::debug() function to help with debugging
7
+ - FEAT: Parse more pattern matching cases
8
+ - FIX: Don't error if ext/natalie_parser/build.log isn't written yet
9
+ - FIX: Fix block association inside call with parentheses
10
+ - FIX: Fix bug negating an already-negative number
11
+ - FIX: Fix bug parsing unary operator as an infix operation
12
+ - FIX: Fix method definition with lonely ensure
13
+ - FIX: Fix parsing endless ranges inside case/when and other odd places
14
+ - FIX: Make sure every token knows if it has preceding whitespace
15
+ - FIX: Parse method calls with constant receiver
16
+
3
17
  ## 1.1.1 (2022-06-04)
4
18
 
5
19
  - FIX: Workaround for clang declspec bug
@@ -8,7 +22,7 @@
8
22
 
9
23
  - CHORE: Add ccache and compiledb for the ext/natalie_parser directory
10
24
  - CHORE: Add tests for numbered block arg shorthand
11
- - FEAT Parse arg forwarding (...) shorthand
25
+ - FEAT: Parse arg forwarding (...) shorthand
12
26
  - FEAT: Parse complex and rational numbers
13
27
  - FIX: Fix panic when closing word array delimiter is not found
14
28
  - 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:
@@ -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
  };
@@ -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
 
@@ -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 {};
@@ -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 &);
@@ -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,6 +771,7 @@ 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:
@@ -770,6 +779,7 @@ public:
770
779
  case Token::Type::PercentLowerW:
771
780
  case Token::Type::PercentUpperI:
772
781
  case Token::Type::PercentUpperW:
782
+ case Token::Type::Plus:
773
783
  case Token::Type::SelfKeyword:
774
784
  case Token::Type::Star:
775
785
  case Token::Type::String:
@@ -824,6 +834,25 @@ public:
824
834
  }
825
835
  }
826
836
 
837
+ bool can_be_range_arg_token() const {
838
+ if (is_closing_token())
839
+ return false;
840
+ if (is_semicolon() || is_eof())
841
+ return false;
842
+ switch (m_type) {
843
+ case Type::ElseKeyword:
844
+ case Type::ElsifKeyword:
845
+ case Type::EndKeyword:
846
+ case Type::InKeyword:
847
+ case Type::ThenKeyword:
848
+ case Type::WhenKeyword:
849
+ // TODO: likely many more cases!
850
+ return false;
851
+ default:
852
+ return true;
853
+ }
854
+ }
855
+
827
856
  void set_literal(const char *literal) { m_literal = new String(literal); }
828
857
  void set_literal(SharedPtr<String> literal) { m_literal = literal; }
829
858
  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 = '1.2.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() {
@@ -11,7 +11,7 @@ Token WordArrayLexer::build_next_token() {
11
11
  return consume_array();
12
12
  case State::DynamicStringBegin:
13
13
  m_state = State::EvaluateBegin;
14
- return Token { Token::Type::String, m_buffer, m_file, m_token_line, m_token_column };
14
+ return Token { Token::Type::String, m_buffer, m_file, m_token_line, m_token_column, m_whitespace_precedes };
15
15
  case State::DynamicStringEnd:
16
16
  if (current_char() == m_stop_char) {
17
17
  advance();
@@ -19,18 +19,18 @@ Token WordArrayLexer::build_next_token() {
19
19
  } else {
20
20
  m_state = State::InProgress;
21
21
  }
22
- return Token { Token::Type::InterpolatedStringEnd, m_file, m_token_line, m_token_column };
22
+ return Token { Token::Type::InterpolatedStringEnd, m_file, m_token_line, m_token_column, m_whitespace_precedes };
23
23
  case State::EvaluateBegin:
24
24
  return start_evaluation();
25
25
  case State::EvaluateEnd:
26
26
  advance(); // }
27
27
  m_state = State::DynamicStringInProgress;
28
- return Token { Token::Type::EvaluateToStringEnd, m_file, m_token_line, m_token_column };
28
+ return Token { Token::Type::EvaluateToStringEnd, m_file, m_token_line, m_token_column, m_whitespace_precedes };
29
29
  case State::EndToken:
30
30
  m_state = State::Done;
31
- return Token { Token::Type::RBracket, m_file, m_cursor_line, m_cursor_column };
31
+ return Token { Token::Type::RBracket, m_file, m_cursor_line, m_cursor_column, m_whitespace_precedes };
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
  }
@@ -70,7 +70,7 @@ Token WordArrayLexer::consume_array() {
70
70
  return dynamic_string_finish();
71
71
  }
72
72
  if (!m_buffer->is_empty()) {
73
- auto token = Token { Token::Type::String, m_buffer, m_file, m_cursor_line, m_cursor_column };
73
+ auto token = Token { Token::Type::String, m_buffer, m_file, m_cursor_line, m_cursor_column, m_whitespace_precedes };
74
74
  advance();
75
75
  return token;
76
76
  }
@@ -97,38 +97,38 @@ Token WordArrayLexer::consume_array() {
97
97
  }
98
98
  }
99
99
 
100
- return Token { Token::Type::UnterminatedWordArray, m_buffer, m_file, m_token_line, m_token_column };
100
+ return Token { Token::Type::UnterminatedWordArray, m_buffer, m_file, m_token_line, m_token_column, m_whitespace_precedes };
101
101
  }
102
102
 
103
103
  Token WordArrayLexer::in_progress_start_dynamic_string() {
104
104
  advance(2); // #{
105
105
  m_state = State::DynamicStringBegin;
106
- return Token { Token::Type::InterpolatedStringBegin, m_file, m_cursor_line, m_cursor_column };
106
+ return Token { Token::Type::InterpolatedStringBegin, m_file, m_cursor_line, m_cursor_column, m_whitespace_precedes };
107
107
  }
108
108
 
109
109
  Token WordArrayLexer::start_evaluation() {
110
110
  m_nested_lexer = new Lexer { *this, '{', '}' };
111
111
  m_state = State::EvaluateEnd;
112
- return Token { Token::Type::EvaluateToStringBegin, m_file, m_token_line, m_token_column };
112
+ return Token { Token::Type::EvaluateToStringBegin, m_file, m_token_line, m_token_column, m_whitespace_precedes };
113
113
  }
114
114
 
115
115
  Token WordArrayLexer::dynamic_string_finish() {
116
116
  if (!m_buffer->is_empty()) {
117
117
  m_state = State::DynamicStringEnd;
118
- return Token { Token::Type::String, m_buffer, m_file, m_cursor_line, m_cursor_column };
118
+ return Token { Token::Type::String, m_buffer, m_file, m_cursor_line, m_cursor_column, m_whitespace_precedes };
119
119
  }
120
120
  m_state = State::InProgress;
121
- return Token { Token::Type::InterpolatedStringEnd, m_file, m_token_line, m_token_column };
121
+ return Token { Token::Type::InterpolatedStringEnd, m_file, m_token_line, m_token_column, m_whitespace_precedes };
122
122
  }
123
123
 
124
124
  Token WordArrayLexer::in_progress_finish() {
125
125
  advance(); // ) or ] or } or whatever
126
126
  if (!m_buffer->is_empty()) {
127
127
  m_state = State::EndToken;
128
- return Token { Token::Type::String, m_buffer, m_file, m_cursor_line, m_cursor_column };
128
+ return Token { Token::Type::String, m_buffer, m_file, m_cursor_line, m_cursor_column, m_whitespace_precedes };
129
129
  }
130
130
  m_state = State::Done;
131
- return Token { Token::Type::RBracket, m_file, m_cursor_line, m_cursor_column };
131
+ return Token { Token::Type::RBracket, m_file, m_cursor_line, m_cursor_column, m_whitespace_precedes };
132
132
  }
133
133
 
134
134
  };