rux 1.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 +7 -0
- data/Gemfile +9 -0
- data/LICENSE +21 -0
- data/README.md +310 -0
- data/Rakefile +14 -0
- data/bin/ruxc +96 -0
- data/lib/rux.rb +73 -0
- data/lib/rux/ast.rb +9 -0
- data/lib/rux/ast/list_node.rb +15 -0
- data/lib/rux/ast/ruby_node.rb +15 -0
- data/lib/rux/ast/string_node.rb +15 -0
- data/lib/rux/ast/tag_node.rb +17 -0
- data/lib/rux/ast/text_node.rb +17 -0
- data/lib/rux/buffer.rb +15 -0
- data/lib/rux/default_tag_builder.rb +20 -0
- data/lib/rux/default_visitor.rb +67 -0
- data/lib/rux/file.rb +27 -0
- data/lib/rux/lex.rb +9 -0
- data/lib/rux/lex/patterns.rb +41 -0
- data/lib/rux/lex/state.rb +33 -0
- data/lib/rux/lex/states.csv +39 -0
- data/lib/rux/lex/transition.rb +22 -0
- data/lib/rux/lexer.rb +64 -0
- data/lib/rux/parser.rb +244 -0
- data/lib/rux/ruby_lexer.rb +143 -0
- data/lib/rux/rux_lexer.rb +157 -0
- data/lib/rux/utils.rb +15 -0
- data/lib/rux/version.rb +3 -0
- data/lib/rux/visitor.rb +33 -0
- data/rux.gemspec +20 -0
- data/spec/parser_spec.rb +229 -0
- data/spec/spec_helper.rb +6 -0
- metadata +102 -0
data/lib/rux.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
# Racc uses LoadErrors for flow control and in certain ruby versions will
|
4
|
+
# print one along with its stack trace when required. To avoid receiving a
|
5
|
+
# long, arresting, and entirely irrelevant stack trace every time you invoke
|
6
|
+
# rux, temporarily redirect STDOUT here and throw away the output.
|
7
|
+
begin
|
8
|
+
old_stdout = $stdout
|
9
|
+
$stdout = StringIO.new
|
10
|
+
require 'racc/parser'
|
11
|
+
ensure
|
12
|
+
$stdout = old_stdout
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'cgi'
|
16
|
+
require 'parser/current'
|
17
|
+
require 'unparser'
|
18
|
+
|
19
|
+
module Rux
|
20
|
+
autoload :AST, 'rux/ast'
|
21
|
+
autoload :Buffer, 'rux/buffer'
|
22
|
+
autoload :Component, 'rux/component'
|
23
|
+
autoload :DefaultTagBuilder, 'rux/default_tag_builder'
|
24
|
+
autoload :DefaultVisitor, 'rux/default_visitor'
|
25
|
+
autoload :File, 'rux/file'
|
26
|
+
autoload :Lex, 'rux/lex'
|
27
|
+
autoload :Lexer, 'rux/lexer'
|
28
|
+
autoload :Parser, 'rux/parser'
|
29
|
+
autoload :RubyLexer, 'rux/ruby_lexer'
|
30
|
+
autoload :RuxLexer, 'rux/rux_lexer'
|
31
|
+
autoload :Utils, 'rux/utils'
|
32
|
+
autoload :Visitor, 'rux/visitor'
|
33
|
+
|
34
|
+
class << self
|
35
|
+
attr_accessor :tag_builder, :buffer
|
36
|
+
|
37
|
+
def to_ruby(str, visitor: default_visitor, pretty: true)
|
38
|
+
ruby_code = visitor.visit(Parser.parse(str))
|
39
|
+
return ruby_code unless pretty
|
40
|
+
|
41
|
+
::Unparser.unparse(
|
42
|
+
::Parser::CurrentRuby.parse(ruby_code)
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
def default_visitor
|
47
|
+
@default_visitor ||= DefaultVisitor.new
|
48
|
+
end
|
49
|
+
|
50
|
+
def default_tag_builder
|
51
|
+
@default_tag_builder ||= DefaultTagBuilder.new
|
52
|
+
end
|
53
|
+
|
54
|
+
def default_buffer
|
55
|
+
@default_buffer ||= Buffer
|
56
|
+
end
|
57
|
+
|
58
|
+
def tag(tag_name, attributes = {}, &block)
|
59
|
+
tag_builder.call(tag_name, attributes, &block)
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_buffer
|
63
|
+
buffer.new
|
64
|
+
end
|
65
|
+
|
66
|
+
def library_paths
|
67
|
+
@library_paths ||= []
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
self.tag_builder = self.default_tag_builder
|
72
|
+
self.buffer = self.default_buffer
|
73
|
+
end
|
data/lib/rux/ast.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Rux
|
2
|
+
module AST
|
3
|
+
class TagNode
|
4
|
+
attr_reader :name, :attrs, :children
|
5
|
+
|
6
|
+
def initialize(name, attrs)
|
7
|
+
@name = name
|
8
|
+
@attrs = attrs
|
9
|
+
@children = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def accept(visitor)
|
13
|
+
visitor.visit_tag(self)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/rux/buffer.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Rux
|
2
|
+
class DefaultTagBuilder
|
3
|
+
def call(tag_name, attributes = {})
|
4
|
+
"<#{tag_name} #{serialize_attrs(attributes)}>" <<
|
5
|
+
(block_given? ? Array(yield) : []).join <<
|
6
|
+
"</#{tag_name}>"
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def serialize_attrs(attributes)
|
12
|
+
''.tap do |result|
|
13
|
+
attributes.each_pair.with_index do |(k, v), idx|
|
14
|
+
result << ' ' unless idx == 0
|
15
|
+
result << "#{k.to_s.gsub('-', '_')}=\"#{CGI.escape_html(v.to_s)}\""
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module Rux
|
4
|
+
class DefaultVisitor < Visitor
|
5
|
+
def visit_list(node)
|
6
|
+
node.children.map { |child| visit(child) }.join
|
7
|
+
end
|
8
|
+
|
9
|
+
def visit_ruby(node)
|
10
|
+
node.code
|
11
|
+
end
|
12
|
+
|
13
|
+
def visit_string(node)
|
14
|
+
node.str
|
15
|
+
end
|
16
|
+
|
17
|
+
def visit_tag(node)
|
18
|
+
''.tap do |result|
|
19
|
+
block_arg = if (as = node.attrs['as'])
|
20
|
+
visit(as)
|
21
|
+
end
|
22
|
+
|
23
|
+
at = node.attrs.each_with_object([]) do |(k, v), ret|
|
24
|
+
next if k == 'as'
|
25
|
+
ret << Utils.attr_to_hash_elem(k, visit(v))
|
26
|
+
end
|
27
|
+
|
28
|
+
if node.name.start_with?(/[A-Z]/)
|
29
|
+
result << "render(#{node.name}.new"
|
30
|
+
|
31
|
+
unless node.attrs.empty?
|
32
|
+
result << "({ #{at.join(', ')} })"
|
33
|
+
end
|
34
|
+
else
|
35
|
+
result << "Rux.tag('#{node.name}'"
|
36
|
+
|
37
|
+
unless node.attrs.empty?
|
38
|
+
result << ", { #{at.join(', ')} }"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
result << ')'
|
43
|
+
|
44
|
+
if node.children.size > 1
|
45
|
+
result << " { "
|
46
|
+
result << "|#{block_arg}| " if block_arg
|
47
|
+
result << "Rux.create_buffer.tap { |_rux_buf_| "
|
48
|
+
|
49
|
+
node.children.each do |child|
|
50
|
+
result << "_rux_buf_ << #{visit(child).strip};"
|
51
|
+
end
|
52
|
+
|
53
|
+
result << " }.to_s }"
|
54
|
+
elsif node.children.size == 1
|
55
|
+
result << ' { '
|
56
|
+
result << "|#{block_arg}| " if block_arg
|
57
|
+
result << visit(node.children.first).strip
|
58
|
+
result << ' }'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def visit_text(node)
|
64
|
+
"\"#{CGI.escape_html(node.text)}\""
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/rux/file.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Rux
|
2
|
+
class File
|
3
|
+
attr_reader :path
|
4
|
+
|
5
|
+
def initialize(path)
|
6
|
+
@path = path
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_ruby(visitor: Rux.default_visitor, **kwargs)
|
10
|
+
Rux.to_ruby(contents, visitor: visitor, **kwargs)
|
11
|
+
end
|
12
|
+
|
13
|
+
def write(outfile = nil, **kwargs)
|
14
|
+
::File.write(outfile || default_outfile, to_ruby(**kwargs))
|
15
|
+
end
|
16
|
+
|
17
|
+
def default_outfile
|
18
|
+
@outfile ||= "#{path.chomp('.rux')}.rb"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def contents
|
24
|
+
@contents ||= ::File.read(path)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/rux/lex.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module Rux
|
2
|
+
module Lex
|
3
|
+
class DefaultPattern
|
4
|
+
def matches?(_)
|
5
|
+
true
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class CharsetPattern
|
10
|
+
def self.parse(str)
|
11
|
+
pairs = str.scan(/(.)-?(.)?/)
|
12
|
+
|
13
|
+
new(
|
14
|
+
pairs.flat_map do |pair|
|
15
|
+
if pair[1]
|
16
|
+
(pair[0]..pair[1]).to_a
|
17
|
+
else
|
18
|
+
[pair[0]]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :chars
|
25
|
+
|
26
|
+
def initialize(chars)
|
27
|
+
@chars = Set.new(chars)
|
28
|
+
end
|
29
|
+
|
30
|
+
def matches?(char)
|
31
|
+
chars.include?(char)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class NegatedCharsetPattern < CharsetPattern
|
36
|
+
def matches?(char)
|
37
|
+
!chars.include?(char)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Rux
|
2
|
+
module Lex
|
3
|
+
class State
|
4
|
+
def self.parse(state_str, transition_strs, inputs)
|
5
|
+
is_terminal = state_str.end_with?('*')
|
6
|
+
state_name = "tRUX_#{state_str.chomp('*').upcase}".to_sym
|
7
|
+
|
8
|
+
transitions = transition_strs.each_with_object([]).with_index do |(ts, ret), idx|
|
9
|
+
ret << Transition.parse(ts, inputs[idx]) if ts
|
10
|
+
end
|
11
|
+
|
12
|
+
new(state_name, is_terminal, transitions)
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :name, :is_terminal, :transitions
|
16
|
+
|
17
|
+
alias_method :terminal?, :is_terminal
|
18
|
+
|
19
|
+
def initialize(name, is_terminal, transitions)
|
20
|
+
@name = name
|
21
|
+
@is_terminal = is_terminal
|
22
|
+
@transitions = transitions
|
23
|
+
@cache = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](chr)
|
27
|
+
@cache[chr] ||= transitions.find do |trans|
|
28
|
+
trans.matches?(chr)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,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],,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,,,,,,,,,,,
|
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
|
+
,,,,,,,,,,,,,
|
18
|
+
attribute_spaces_body,,attribute_spaces[0],attribute_spaces[0],attribute_spaces[0],attribute_spaces_body,,,,,,,,
|
19
|
+
attribute_spaces*,,attribute_name_body,tag_close_start,tag_self_closing_start,,,,,,,,,
|
20
|
+
attribute_name_body,,attribute_name_body,attribute_name[0],attribute_name[0],attribute_name[0],attribute_name[0],,,,,,,
|
21
|
+
attribute_name*,,,tag_open_end,tag_self_closing_start,attribute_equals_spaces_body,attribute_equals,,,,,,,
|
22
|
+
attribute_equals_spaces_body,,,attribute_equals_spaces[0],,attribute_equals_spaces_body,attribute_equals_spaces[0],,,,,,,attribute_equals_spaces[0]
|
23
|
+
attribute_equals_spaces*,,,tag_open_end,tag_self_closing_start,,attribute_equals,,,,,,,attribute_name_body
|
24
|
+
attribute_equals*,,attribute_uq_body,,,attribute_value_spaces_body[0],,attribute_dq_body,attribute_sq_body,,,attribute_value_ruby_code_start,,
|
25
|
+
attribute_value_spaces_body,,attribute_value_spaces[0],,,attribute_value_spaces_body,,attribute_value_spaces[0],attribute_value_spaces[0],,,attribute_value_spaces[0],,
|
26
|
+
attribute_value_spaces*,,attribute_uq_body,,,,,attribute_dq_body,attribute_sq_body,,,attribute_value_ruby_code_start,,
|
27
|
+
attribute_dq_body,,,,,,,attribute_value,,attribute_dq_body,,,,
|
28
|
+
attribute_sq_body,,,,,,,,attribute_value,,attribute_sq_body,,,
|
29
|
+
attribute_uq_body,,attribute_uq_body,,,attribute_value,,,,,,,,
|
30
|
+
attribute_value_ruby_code_start*,,,,,,,,,,,,,attribute_value_ruby_code
|
31
|
+
attribute_value_ruby_code*,,,,,,,,,,,,attribute_value_ruby_code_end,
|
32
|
+
attribute_value_ruby_code_end*,,attribute_name,tag_open_end,tag_self_closing_start,attribute_spaces_body,,,,,,,,
|
33
|
+
attribute_value*,,attribute_name,tag_open_end,tag_self_closing_start,attribute_spaces_body,,attribute_value_ending,attribute_value_ending,,,,,
|
34
|
+
,,,,,,,,,,,,,
|
35
|
+
literal_body,literal[0],,,,,,,,,,literal[0],,literal_body
|
36
|
+
literal*,,,,,,,,,,,literal_ruby_code_start,,
|
37
|
+
literal_ruby_code_start*,,,,,,,,,,,,,literal_ruby_code[0]
|
38
|
+
literal_ruby_code*,,,,,,,,,,,,literal_ruby_code_end,
|
39
|
+
literal_ruby_code_end*,,,,,,,,,,,,,
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Rux
|
2
|
+
module Lex
|
3
|
+
class Transition
|
4
|
+
attr_reader :input, :to_state, :advance_count
|
5
|
+
|
6
|
+
def self.parse(str, input)
|
7
|
+
to_state, advance_count = str.match(/\A(\w+)\[?(-?\d+)?\]?/).captures
|
8
|
+
new(input, :"tRUX_#{to_state.upcase}", (advance_count || 1).to_i)
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(input, to_state, advance_count)
|
12
|
+
@input = input
|
13
|
+
@to_state = to_state
|
14
|
+
@advance_count = advance_count
|
15
|
+
end
|
16
|
+
|
17
|
+
def matches?(chr)
|
18
|
+
input.matches?(chr)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|