rux 1.1.2 → 1.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 +24 -0
- data/Gemfile +8 -1
- data/README.md +88 -0
- data/bin/ruxc +47 -5
- data/bin/ruxlex +13 -0
- data/lib/rux/ast/attr_node.rb +22 -0
- data/lib/rux/ast/attrs_node.rb +30 -0
- data/lib/rux/ast/fragment_node.rb +15 -0
- data/lib/rux/ast/root_node.rb +19 -0
- data/lib/rux/ast/ruby_attr_node.rb +28 -0
- data/lib/rux/ast/string_node.rb +4 -2
- data/lib/rux/ast/tag_node.rb +10 -1
- data/lib/rux/ast/text_node.rb +2 -1
- data/lib/rux/ast.rb +10 -5
- data/lib/rux/buffer.rb +21 -3
- data/lib/rux/default_tag_builder.rb +15 -6
- data/lib/rux/default_visitor.rb +100 -23
- data/lib/rux/lex/states.csv +51 -39
- data/lib/rux/lexer.rb +13 -0
- data/lib/rux/parser.rb +112 -28
- data/lib/rux/ruby_lexer.rb +32 -8
- data/lib/rux/rux_lexer.rb +16 -4
- data/lib/rux/version.rb +1 -1
- data/lib/rux/visitor.rb +16 -0
- data/lib/rux.rb +2 -3
- data/rux.gemspec +3 -1
- data/spec/parser/attributes_spec.rb +121 -0
- data/spec/parser/fragment_spec.rb +64 -0
- data/spec/parser/html_safety_spec.rb +13 -0
- data/spec/parser/slot_spec.rb +40 -0
- data/spec/parser/tag_spec.rb +174 -0
- data/spec/parser_spec.rb +56 -232
- data/spec/render_spec.rb +70 -7
- data/spec/spec_helper.rb +19 -32
- data/spec/support/components/args_component.rb +14 -0
- data/spec/support/components/data_component.rb +11 -0
- data/spec/support/components/table_component.rb +23 -0
- data/spec/support/components/test_component.rb +7 -0
- data/spec/support/view_component/base.rb +59 -0
- metadata +36 -8
data/lib/rux/lex/states.csv
CHANGED
|
@@ -1,39 +1,51 @@
|
|
|
1
|
-
,[<],[a-zA-Z0-9_-_---:-:],[>],[/],(space),[=],"[""]",['],"[^""]",[^'],[{],[}],(default)
|
|
2
|
-
start,tag_open_test
|
|
3
|
-
tag_open_test
|
|
4
|
-
tag_open_start
|
|
5
|
-
tag_open_body
|
|
6
|
-
tag_open
|
|
7
|
-
tag_open_end
|
|
8
|
-
tag_close_start
|
|
9
|
-
tag_close_body
|
|
10
|
-
tag_close
|
|
11
|
-
tag_close_spaces_body
|
|
12
|
-
tag_close_spaces
|
|
13
|
-
tag_close_end
|
|
14
|
-
tag_self_closing
|
|
15
|
-
tag_self_closing_start
|
|
16
|
-
tag_self_closing_end
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
attribute_equals
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
1
|
+
,[<],[@-@.-.],[a-zA-Z0-9_-_---:-:],[>],[/],(space),[=],"[""]",['],"[^""]",[^'],[{],[}],(default)
|
|
2
|
+
start,tag_open_test,,,,,,,,,,,literal_ruby_code_start,,literal_body
|
|
3
|
+
tag_open_test,,,tag_open_start[0],fragment_open,tag_close_start,,,,,,,,,
|
|
4
|
+
tag_open_start*,,,tag_open_body,,,,,,,,,,,
|
|
5
|
+
tag_open_body,,,tag_open_body,tag_open[0],tag_self_closing[0],tag_open[0],,,,,,,,
|
|
6
|
+
tag_open*,,,,tag_open_end,,attribute_spaces_body,,,,,,,,
|
|
7
|
+
tag_open_end*,,,,,,,,,,,,,,
|
|
8
|
+
tag_close_start*,,,tag_close_body,fragment_close,,,,,,,,,,
|
|
9
|
+
tag_close_body,,,tag_close_body,tag_close[0],,tag_close[0],,,,,,,,
|
|
10
|
+
tag_close*,,,,tag_close_end,tag_self_closing[0],tag_close_spaces_body,,,,,,,,
|
|
11
|
+
tag_close_spaces_body,,,,tag_close_spaces[0],,tag_close_spaces_body,,,,,,,,
|
|
12
|
+
tag_close_spaces*,,,,tag_close_end,,,,,,,,,,
|
|
13
|
+
tag_close_end*,,,,,,,,,,,,,,
|
|
14
|
+
tag_self_closing*,,,,,tag_self_closing_start,,,,,,,,,
|
|
15
|
+
tag_self_closing_start,,,,tag_self_closing_end,,,,,,,,,,
|
|
16
|
+
tag_self_closing_end*,,,,,,,,,,,,,,
|
|
17
|
+
fragment_open*,,,,,,,,,,,,,,
|
|
18
|
+
fragment_close*,,,,,,,,,,,,,,
|
|
19
|
+
,,,,,,,,,,,,,,
|
|
20
|
+
attribute_spaces_body,,attribute_spaces[0],attribute_spaces[0],attribute_spaces[0],attribute_spaces[0],attribute_spaces_body,,,,,,attribute_spaces[0],,
|
|
21
|
+
attribute_spaces*,,attribute_name_sigil,attribute_name_body,tag_open_end,tag_self_closing_start,,,,,,,attribute_ruby_code_start,,
|
|
22
|
+
attribute_name_sigil,,,,,,,,,,,,,,attribute_name_body
|
|
23
|
+
attribute_name_body,,,attribute_name_body,attribute_name[0],attribute_name[0],attribute_name[0],attribute_name[0],,,,,,,
|
|
24
|
+
attribute_name*,,,,tag_open_end,tag_self_closing_start,attribute_equals_spaces_body,attribute_equals,,,,,,,
|
|
25
|
+
attribute_equals_spaces_body,,,,attribute_equals_spaces[0],,attribute_equals_spaces_body,attribute_equals_spaces[0],,,,,,,attribute_equals_spaces[0]
|
|
26
|
+
attribute_equals_spaces*,,,,tag_open_end,tag_self_closing_start,,attribute_equals,,,,,,,attribute_name_sigil
|
|
27
|
+
attribute_equals*,,,attribute_value_uq_body,,,attribute_value_spaces_body[0],,attribute_value_dq_start,attribute_value_sq_start,,,attribute_value_ruby_code_start,,
|
|
28
|
+
attribute_value_spaces_body,,,attribute_value_spaces[0],attribute_value_spaces[0],attribute_value_spaces[0],attribute_value_spaces_body,,attribute_value_spaces[0],attribute_value_spaces[0],,,attribute_value_spaces[0],,
|
|
29
|
+
attribute_value_spaces*,,,attribute_value_uq_body,tag_open_end,tag_self_closing_start,,,attribute_value_dq_start,attribute_value_sq_start,,,attribute_value_ruby_code_start,,
|
|
30
|
+
attribute_value_dq_start*,,,,,,,,,,attribute_value_dq_body,,,,
|
|
31
|
+
attribute_value_sq_start*,,,,,,,,,,,attribute_value_sq_body,,,
|
|
32
|
+
attribute_value_dq_body,,,,,,,,attribute_dq_value[0],,attribute_value_dq_body,,,,
|
|
33
|
+
attribute_value_sq_body,,,,,,,,,attribute_sq_value[0],,attribute_value_sq_body,,,
|
|
34
|
+
attribute_value_uq_body,,,attribute_value_uq_body,attribute_uq_value[0],attrIbute_uq_value[0],attribute_uq_value[0],,,,,,,,
|
|
35
|
+
attribute_value_dq_end*,,,attribute_name,tag_open_end,tag_self_closing_start,attribute_spaces_body,,,,,,,,
|
|
36
|
+
attribute_value_sq_end*,,,attribute_name,tag_open_end,tag_self_closing_start,attribute_spaces_body,,,,,,,,
|
|
37
|
+
attribute_value_ruby_code_start*,,,,,,,,,,,,,,attribute_value_ruby_code
|
|
38
|
+
attribute_value_ruby_code*,,,,,,,,,,,,,attribute_value_ruby_code_end,
|
|
39
|
+
attribute_value_ruby_code_end*,,,attribute_name,tag_open_end,tag_self_closing_start,attribute_spaces_body,,,,,,,,
|
|
40
|
+
attribute_dq_value*,,,,,,,,attribute_value_dq_end,,,,,,
|
|
41
|
+
attribute_sq_value*,,,,,,,,,attribute_value_sq_end,,,,,
|
|
42
|
+
attribute_uq_value*,,,attribute_name,tag_open_end,tag_self_closing_start,attribute_spaces_body,,,,,,,,
|
|
43
|
+
attribute_ruby_code_start*,,,,,,,,,,,,,,attribute_ruby_code[0]
|
|
44
|
+
attribute_ruby_code*,,,,,,,,,,,,,attribute_ruby_code_end,
|
|
45
|
+
attribute_ruby_code_end*,,,attribute_name,tag_open_end,tag_self_closing_start,attribute_spaces_body,,,,,,,,
|
|
46
|
+
,,,,,,,,,,,,,,
|
|
47
|
+
literal_body,literal[0],,,,,,,,,,,literal[0],,literal_body
|
|
48
|
+
literal*,,,,,,,,,,,,literal_ruby_code_start,,
|
|
49
|
+
literal_ruby_code_start*,,,,,,,,,,,,,,literal_ruby_code[0]
|
|
50
|
+
literal_ruby_code*,,,,,,,,,,,,,literal_ruby_code_end,
|
|
51
|
+
literal_ruby_code_end*,,,,,,,,,,,,,,
|
data/lib/rux/lexer.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'parser'
|
|
2
|
+
|
|
1
3
|
module Rux
|
|
2
4
|
class Lexer
|
|
3
5
|
class EOFError < StandardError; end
|
|
@@ -5,6 +7,13 @@ module Rux
|
|
|
5
7
|
|
|
6
8
|
attr_reader :source_buffer
|
|
7
9
|
|
|
10
|
+
class << self
|
|
11
|
+
def lex(str)
|
|
12
|
+
buffer = ::Parser::Source::Buffer.new('(source)', source: str)
|
|
13
|
+
new(buffer).to_a
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
8
17
|
def initialize(source_buffer)
|
|
9
18
|
@source_buffer = source_buffer
|
|
10
19
|
@stack = [RubyLexer.new(source_buffer, 0)]
|
|
@@ -17,6 +26,10 @@ module Rux
|
|
|
17
26
|
[nil, ['$eof']]
|
|
18
27
|
end
|
|
19
28
|
|
|
29
|
+
def to_a
|
|
30
|
+
@generator.to_a
|
|
31
|
+
end
|
|
32
|
+
|
|
20
33
|
private
|
|
21
34
|
|
|
22
35
|
def each_token
|
data/lib/rux/parser.rb
CHANGED
|
@@ -8,14 +8,18 @@ module Rux
|
|
|
8
8
|
class << self
|
|
9
9
|
def parse_file(path)
|
|
10
10
|
buffer = ::Parser::Source::Buffer.new(path).read
|
|
11
|
-
|
|
12
|
-
new(lexer).parse
|
|
11
|
+
new(make_lexer(buffer)).parse
|
|
13
12
|
end
|
|
14
13
|
|
|
15
14
|
def parse(str)
|
|
16
15
|
buffer = ::Parser::Source::Buffer.new('(source)', source: str)
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
new(make_lexer(buffer)).parse
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def make_lexer(buffer)
|
|
22
|
+
::Rux::Lexer.new(buffer)
|
|
19
23
|
end
|
|
20
24
|
end
|
|
21
25
|
|
|
@@ -26,6 +30,12 @@ module Rux
|
|
|
26
30
|
end
|
|
27
31
|
|
|
28
32
|
def parse
|
|
33
|
+
AST::RootNode.new(list)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def list
|
|
29
39
|
curlies = 1
|
|
30
40
|
children = []
|
|
31
41
|
|
|
@@ -34,9 +44,9 @@ module Rux
|
|
|
34
44
|
break unless type
|
|
35
45
|
|
|
36
46
|
case type
|
|
37
|
-
when :tLCURLY, :tLBRACE, :tRUX_LITERAL_RUBY_CODE_START
|
|
47
|
+
when :tLCURLY, :tLBRACE, :tRUX_LITERAL_RUBY_CODE_START, :tRUX_ATTRIBUTE_RUBY_CODE_START
|
|
38
48
|
curlies += 1
|
|
39
|
-
when :tRCURLY, :tRBRACE, :tRUX_LITERAL_RUBY_CODE_END
|
|
49
|
+
when :tRCURLY, :tRBRACE, :tRUX_LITERAL_RUBY_CODE_END, :tRUX_ATTRIBUTE_RUBY_CODE_END
|
|
40
50
|
curlies -= 1
|
|
41
51
|
end
|
|
42
52
|
|
|
@@ -46,6 +56,8 @@ module Rux
|
|
|
46
56
|
children << rb
|
|
47
57
|
elsif type_of(current) == :tRUX_TAG_OPEN_START
|
|
48
58
|
children << tag
|
|
59
|
+
elsif type_of(current) == :tRUX_FRAGMENT_OPEN
|
|
60
|
+
children << fragment
|
|
49
61
|
else
|
|
50
62
|
raise UnexpectedTokenError,
|
|
51
63
|
'expected ruby code or the start of a rux tag but found '\
|
|
@@ -56,8 +68,6 @@ module Rux
|
|
|
56
68
|
AST::ListNode.new(children)
|
|
57
69
|
end
|
|
58
70
|
|
|
59
|
-
private
|
|
60
|
-
|
|
61
71
|
def ruby
|
|
62
72
|
ruby_start = pos_of(current).begin_pos
|
|
63
73
|
|
|
@@ -79,7 +89,7 @@ module Rux
|
|
|
79
89
|
|
|
80
90
|
if pos_of(current).begin_pos != ruby_start
|
|
81
91
|
AST::RubyNode.new(
|
|
82
|
-
@lexer.source_buffer.source[ruby_start...(pos_of(current).
|
|
92
|
+
@lexer.source_buffer.source[ruby_start...(pos_of(current).begin_pos)]
|
|
83
93
|
)
|
|
84
94
|
end
|
|
85
95
|
end
|
|
@@ -93,7 +103,8 @@ module Rux
|
|
|
93
103
|
attrs = attributes
|
|
94
104
|
maybe_consume(:tRUX_ATTRIBUTE_SPACES)
|
|
95
105
|
maybe_consume(:tRUX_TAG_OPEN_END)
|
|
96
|
-
tag_node = AST::TagNode.new(tag_name, attrs)
|
|
106
|
+
tag_node = AST::TagNode.new(tag_name, attrs, tag_pos)
|
|
107
|
+
attrs.each { |attr_node| attr_node.tag_node = tag_node }
|
|
97
108
|
|
|
98
109
|
if is?(:tRUX_TAG_SELF_CLOSING_END)
|
|
99
110
|
consume(:tRUX_TAG_SELF_CLOSING_END)
|
|
@@ -103,12 +114,7 @@ module Rux
|
|
|
103
114
|
@stack.push(tag_name)
|
|
104
115
|
|
|
105
116
|
until is?(:tRUX_TAG_CLOSE_START)
|
|
106
|
-
|
|
107
|
-
lit = literal
|
|
108
|
-
tag_node.children << lit if lit
|
|
109
|
-
else
|
|
110
|
-
tag_node.children << tag
|
|
111
|
-
end
|
|
117
|
+
populate_next_child(tag_node)
|
|
112
118
|
end
|
|
113
119
|
|
|
114
120
|
consume(:tRUX_TAG_CLOSE_START)
|
|
@@ -131,44 +137,108 @@ module Rux
|
|
|
131
137
|
tag_node
|
|
132
138
|
end
|
|
133
139
|
|
|
140
|
+
def populate_next_child(node)
|
|
141
|
+
if is?(:tRUX_LITERAL, :tRUX_LITERAL_RUBY_CODE_START)
|
|
142
|
+
lit = literal
|
|
143
|
+
node.children << lit if lit
|
|
144
|
+
elsif is?(:tRUX_FRAGMENT_OPEN)
|
|
145
|
+
node.children << fragment
|
|
146
|
+
else
|
|
147
|
+
node.children << tag
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
134
151
|
def attributes
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
152
|
+
pos = pos_of(current)
|
|
153
|
+
|
|
154
|
+
attrs = [].tap do |attrs|
|
|
155
|
+
loop do
|
|
156
|
+
attr = case type_of(current)
|
|
157
|
+
when :tRUX_ATTRIBUTE_NAME
|
|
158
|
+
attribute
|
|
159
|
+
when :tRUX_ATTRIBUTE_RUBY_CODE_START
|
|
160
|
+
ruby_attribute
|
|
161
|
+
end
|
|
139
162
|
|
|
140
163
|
maybe_consume(:tRUX_ATTRIBUTE_SPACES)
|
|
164
|
+
|
|
165
|
+
if attr
|
|
166
|
+
attrs << attr
|
|
167
|
+
else
|
|
168
|
+
break
|
|
169
|
+
end
|
|
141
170
|
end
|
|
142
171
|
end
|
|
172
|
+
|
|
173
|
+
AST::AttrsNode.new(attrs, pos)
|
|
143
174
|
end
|
|
144
175
|
|
|
145
176
|
def attribute
|
|
146
|
-
maybe_consume(:tRUX_ATTRIBUTE_SPACES)
|
|
147
177
|
attr_name = text_of(current)
|
|
178
|
+
attr_pos = pos_of(current)
|
|
148
179
|
consume(:tRUX_ATTRIBUTE_NAME)
|
|
149
180
|
maybe_consume(:tRUX_ATTRIBUTE_EQUALS_SPACES)
|
|
150
181
|
|
|
151
182
|
attr_value = if maybe_consume(:tRUX_ATTRIBUTE_EQUALS)
|
|
152
183
|
maybe_consume(:tRUX_ATTRIBUTE_VALUE_SPACES)
|
|
153
|
-
attribute_value
|
|
184
|
+
attribute_value.tap do
|
|
185
|
+
maybe_consume(:tRUX_ATTRIBUTE_VALUE_SPACES)
|
|
186
|
+
end
|
|
154
187
|
else
|
|
155
188
|
# if no equals sign, assume boolean attribute
|
|
156
|
-
AST::StringNode.new(
|
|
189
|
+
AST::StringNode.new('true', :none, nil)
|
|
157
190
|
end
|
|
158
191
|
|
|
159
|
-
|
|
192
|
+
AST::AttrNode.new(attr_name, attr_value, attr_pos)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def ruby_attribute
|
|
196
|
+
consume(:tRUX_ATTRIBUTE_RUBY_CODE_START)
|
|
197
|
+
|
|
198
|
+
AST::RubyAttrNode.new(ruby).tap do
|
|
199
|
+
consume(:tRUX_ATTRIBUTE_RUBY_CODE_END)
|
|
200
|
+
end
|
|
160
201
|
end
|
|
161
202
|
|
|
162
203
|
def attribute_value
|
|
163
204
|
if is?(:tRUX_ATTRIBUTE_VALUE_RUBY_CODE_START)
|
|
164
205
|
attr_ruby_code
|
|
165
206
|
else
|
|
166
|
-
|
|
167
|
-
|
|
207
|
+
case type_of(current)
|
|
208
|
+
when :tRUX_ATTRIBUTE_VALUE_DQ_START
|
|
209
|
+
attribute_value_dq
|
|
210
|
+
when :tRUX_ATTRIBUTE_VALUE_SQ_START
|
|
211
|
+
attribute_value_sq
|
|
212
|
+
when :tRUX_ATTRIBUTE_UQ_VALUE
|
|
213
|
+
attribute_value_uq
|
|
168
214
|
end
|
|
169
215
|
end
|
|
170
216
|
end
|
|
171
217
|
|
|
218
|
+
def attribute_value_dq
|
|
219
|
+
consume(:tRUX_ATTRIBUTE_VALUE_DQ_START)
|
|
220
|
+
|
|
221
|
+
AST::StringNode.new(text_of(current), :double, pos_of(current)).tap do
|
|
222
|
+
consume(:tRUX_ATTRIBUTE_DQ_VALUE)
|
|
223
|
+
consume(:tRUX_ATTRIBUTE_VALUE_DQ_END)
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def attribute_value_sq
|
|
228
|
+
consume(:tRUX_ATTRIBUTE_VALUE_SQ_START)
|
|
229
|
+
|
|
230
|
+
AST::StringNode.new(text_of(current), :single, pos_of(current)).tap do
|
|
231
|
+
consume(:tRUX_ATTRIBUTE_SQ_VALUE)
|
|
232
|
+
consume(:tRUX_ATTRIBUTE_VALUE_SQ_END)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def attribute_value_uq
|
|
237
|
+
AST::StringNode.new(text_of(current), :none, pos_of(current)).tap do
|
|
238
|
+
consume(:tRUX_ATTRIBUTE_UQ_VALUE)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
172
242
|
def attr_ruby_code
|
|
173
243
|
consume(:tRUX_ATTRIBUTE_VALUE_RUBY_CODE_START)
|
|
174
244
|
|
|
@@ -177,13 +247,27 @@ module Rux
|
|
|
177
247
|
end
|
|
178
248
|
end
|
|
179
249
|
|
|
250
|
+
def fragment
|
|
251
|
+
consume(:tRUX_FRAGMENT_OPEN)
|
|
252
|
+
|
|
253
|
+
AST::FragmentNode.new.tap do |fragment_node|
|
|
254
|
+
until is?(:tRUX_TAG_CLOSE_START)
|
|
255
|
+
populate_next_child(fragment_node)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
consume(:tRUX_TAG_CLOSE_START)
|
|
259
|
+
consume(:tRUX_FRAGMENT_CLOSE)
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
180
263
|
def literal
|
|
181
264
|
if is?(:tRUX_LITERAL_RUBY_CODE_START)
|
|
182
265
|
literal_ruby_code
|
|
183
266
|
else
|
|
184
267
|
lit = squeeze_lit(text_of(current))
|
|
268
|
+
pos = pos_of(current)
|
|
185
269
|
consume(:tRUX_LITERAL)
|
|
186
|
-
AST::TextNode.new(lit) unless lit.empty?
|
|
270
|
+
AST::TextNode.new(lit, pos) unless lit.empty?
|
|
187
271
|
end
|
|
188
272
|
end
|
|
189
273
|
|
|
@@ -197,7 +281,7 @@ module Rux
|
|
|
197
281
|
def literal_ruby_code
|
|
198
282
|
consume(:tRUX_LITERAL_RUBY_CODE_START)
|
|
199
283
|
|
|
200
|
-
|
|
284
|
+
list.tap do
|
|
201
285
|
consume(:tRUX_LITERAL_RUBY_CODE_END)
|
|
202
286
|
end
|
|
203
287
|
end
|
data/lib/rux/ruby_lexer.rb
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
module Rux
|
|
2
|
+
class TokenArrayProxy < Array
|
|
3
|
+
def initialize(rux_token_queue)
|
|
4
|
+
@rux_token_queue = rux_token_queue
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def push(token)
|
|
8
|
+
if token[0] == :tCOMMENT
|
|
9
|
+
@rux_token_queue.push(token)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
super
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
2
16
|
class RubyLexer < ::Parser::Lexer
|
|
3
17
|
# These are populated when ::Parser::Lexer loads and are therefore
|
|
4
18
|
# not inherited. We have to copy them over manually.
|
|
@@ -15,6 +29,8 @@ module Rux
|
|
|
15
29
|
@generator = to_enum(:each_token)
|
|
16
30
|
@rux_token_queue = []
|
|
17
31
|
@p = init_pos
|
|
32
|
+
|
|
33
|
+
self.tokens = TokenArrayProxy.new(@rux_token_queue)
|
|
18
34
|
end
|
|
19
35
|
|
|
20
36
|
alias_method :advance_orig, :advance
|
|
@@ -27,6 +43,7 @@ module Rux
|
|
|
27
43
|
@ts = @te = @p = pos
|
|
28
44
|
@eof = false
|
|
29
45
|
@rux_token_queue.clear
|
|
46
|
+
clear_queue
|
|
30
47
|
populate_queue
|
|
31
48
|
end
|
|
32
49
|
|
|
@@ -44,13 +61,7 @@ module Rux
|
|
|
44
61
|
end
|
|
45
62
|
|
|
46
63
|
def each_token(&block)
|
|
47
|
-
|
|
48
|
-
# ahead by 1 token; that's why the first element in @rux_token_queue is
|
|
49
|
-
# yielded immediately. If the lexer _starts_ at a rux tag however,
|
|
50
|
-
# lookahead is a lot more difficult. To mitigate, we insert a dummy skip
|
|
51
|
-
# token here. That way, at_rux? checks the right tokens in the queue and
|
|
52
|
-
# correctly identifies the start of a rux tag.
|
|
53
|
-
@rux_token_queue << [:tSKIP, ['$skip', make_range(@p, @p)]]
|
|
64
|
+
clear_queue
|
|
54
65
|
|
|
55
66
|
@eof = false
|
|
56
67
|
curlies = 1
|
|
@@ -74,7 +85,7 @@ module Rux
|
|
|
74
85
|
type, (_, pos) = token
|
|
75
86
|
|
|
76
87
|
case type
|
|
77
|
-
when :tLCURLY, :tLBRACE
|
|
88
|
+
when :tLCURLY, :tLBRACE, :tLAMBEG
|
|
78
89
|
curlies += 1
|
|
79
90
|
when :tRCURLY, :tRBRACE
|
|
80
91
|
curlies -= 1
|
|
@@ -111,12 +122,25 @@ module Rux
|
|
|
111
122
|
end
|
|
112
123
|
end
|
|
113
124
|
|
|
125
|
+
def clear_queue
|
|
126
|
+
@rux_token_queue.clear
|
|
127
|
+
|
|
128
|
+
# We detect whether or not we're at the beginning of a rux tag by looking
|
|
129
|
+
# ahead by 1 token; that's why the first element in @rux_token_queue is
|
|
130
|
+
# yielded immediately. If the lexer _starts_ at a rux tag however,
|
|
131
|
+
# lookahead is a lot more difficult. To mitigate, we insert a dummy skip
|
|
132
|
+
# token here. That way, at_rux? checks the right tokens in the queue and
|
|
133
|
+
# correctly identifies the start of a rux tag.
|
|
134
|
+
@rux_token_queue << [:tSKIP, ['$skip', make_range(@p, @p)]]
|
|
135
|
+
end
|
|
136
|
+
|
|
114
137
|
def at_rux?
|
|
115
138
|
at_lt? && !at_inheritance?
|
|
116
139
|
end
|
|
117
140
|
|
|
118
141
|
def at_lt?
|
|
119
142
|
is?(@rux_token_queue[1], :tLT) && (
|
|
143
|
+
is?(@rux_token_queue[2], :tGT) ||
|
|
120
144
|
is?(@rux_token_queue[2], :tCONSTANT) ||
|
|
121
145
|
is?(@rux_token_queue[2], :tIDENTIFIER)
|
|
122
146
|
)
|
data/lib/rux/rux_lexer.rb
CHANGED
|
@@ -2,6 +2,17 @@ require 'csv'
|
|
|
2
2
|
|
|
3
3
|
module Rux
|
|
4
4
|
class RuxLexer
|
|
5
|
+
RUBY_CODE_STATES = [
|
|
6
|
+
# ruby code in attributes, eg. <div {**kwargs}>
|
|
7
|
+
:tRUX_ATTRIBUTE_RUBY_CODE,
|
|
8
|
+
|
|
9
|
+
# ruby code in attribute values, eg. <div foo={bar}>
|
|
10
|
+
:tRUX_ATTRIBUTE_VALUE_RUBY_CODE,
|
|
11
|
+
|
|
12
|
+
# ruby code in tag bodies, eg. <div>{foo}</div>
|
|
13
|
+
:tRUX_LITERAL_RUBY_CODE
|
|
14
|
+
].freeze
|
|
15
|
+
|
|
5
16
|
class << self
|
|
6
17
|
# See: https://docs.google.com/spreadsheets/d/11ikKuySIKoaj-kFIfhlzebUwH31cRt_1flGjWfk7RMg
|
|
7
18
|
def state_table
|
|
@@ -87,11 +98,13 @@ module Rux
|
|
|
87
98
|
case state
|
|
88
99
|
when :tRUX_TAG_OPEN, :tRUX_TAG_SELF_CLOSING
|
|
89
100
|
tag_stack.push(text)
|
|
101
|
+
when :tRUX_FRAGMENT_OPEN
|
|
102
|
+
tag_stack.push(:__fragment)
|
|
90
103
|
when :tRUX_TAG_CLOSE
|
|
91
104
|
tag_stack.pop
|
|
92
105
|
when :tRUX_TAG_CLOSE_END
|
|
93
106
|
break if tag_stack.empty?
|
|
94
|
-
when :tRUX_TAG_SELF_CLOSING_END
|
|
107
|
+
when :tRUX_TAG_SELF_CLOSING_END, :tRUX_FRAGMENT_CLOSE
|
|
95
108
|
tag_stack.pop
|
|
96
109
|
break if tag_stack.empty?
|
|
97
110
|
end
|
|
@@ -110,7 +123,7 @@ module Rux
|
|
|
110
123
|
|
|
111
124
|
unless cur_trans
|
|
112
125
|
raise Rux::Lexer::TransitionError,
|
|
113
|
-
"no transition found from #{cur_state} at position #{@p} while "\
|
|
126
|
+
"no transition found from #{cur_state} for #{chr.inspect} at position #{@p} while "\
|
|
114
127
|
'lexing rux code'
|
|
115
128
|
end
|
|
116
129
|
|
|
@@ -150,8 +163,7 @@ module Rux
|
|
|
150
163
|
# bodies. Eventually I'd like to also allow passing a Ruby hash to
|
|
151
164
|
# dynamically specify attributes, but we're not there yet.
|
|
152
165
|
def ruby_code?(state)
|
|
153
|
-
state
|
|
154
|
-
state == :tRUX_LITERAL_RUBY_CODE
|
|
166
|
+
RUBY_CODE_STATES.include?(state)
|
|
155
167
|
end
|
|
156
168
|
end
|
|
157
169
|
end
|
data/lib/rux/version.rb
CHANGED
data/lib/rux/visitor.rb
CHANGED
|
@@ -4,6 +4,10 @@ module Rux
|
|
|
4
4
|
node.accept(self)
|
|
5
5
|
end
|
|
6
6
|
|
|
7
|
+
def visit_root(node)
|
|
8
|
+
visit_children(node)
|
|
9
|
+
end
|
|
10
|
+
|
|
7
11
|
def visit_list(node)
|
|
8
12
|
visit_children(node)
|
|
9
13
|
end
|
|
@@ -20,6 +24,18 @@ module Rux
|
|
|
20
24
|
visit_children(node)
|
|
21
25
|
end
|
|
22
26
|
|
|
27
|
+
def visit_attrs(node)
|
|
28
|
+
visit_children(node)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def visit_attr(node)
|
|
32
|
+
visit_children(node)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def visit_fragment(node)
|
|
36
|
+
visit_children(node)
|
|
37
|
+
end
|
|
38
|
+
|
|
23
39
|
def visit_text(node)
|
|
24
40
|
visit_children(node)
|
|
25
41
|
end
|
data/lib/rux.rb
CHANGED
|
@@ -38,9 +38,8 @@ module Rux
|
|
|
38
38
|
ruby_code = visitor.visit(Parser.parse(str))
|
|
39
39
|
return ruby_code unless pretty
|
|
40
40
|
|
|
41
|
-
::
|
|
42
|
-
|
|
43
|
-
)
|
|
41
|
+
ast, comments = *::Parser::CurrentRuby.parse_with_comments(ruby_code)
|
|
42
|
+
::Unparser.unparse(ast, comments: comments)
|
|
44
43
|
end
|
|
45
44
|
|
|
46
45
|
def default_visitor
|
data/rux.gemspec
CHANGED
|
@@ -10,11 +10,13 @@ Gem::Specification.new do |s|
|
|
|
10
10
|
s.description = s.summary = 'A jsx-inspired way to write view components.'
|
|
11
11
|
s.platform = Gem::Platform::RUBY
|
|
12
12
|
|
|
13
|
+
s.add_dependency 'onload', '~> 1.1'
|
|
13
14
|
s.add_dependency 'parser', '~> 3.0'
|
|
14
|
-
s.add_dependency 'unparser', '~> 0.
|
|
15
|
+
s.add_dependency 'unparser', '~> 0.8'
|
|
15
16
|
|
|
16
17
|
s.require_path = 'lib'
|
|
17
18
|
s.executables << 'ruxc'
|
|
19
|
+
s.executables << 'ruxlex'
|
|
18
20
|
|
|
19
21
|
s.files = Dir['{lib,spec}/**/*', 'Gemfile', 'LICENSE', 'CHANGELOG.md', 'README.md', 'Rakefile', 'rux.gemspec']
|
|
20
22
|
end
|